knitr::opts_chunk$set(echo = TRUE)
library(latex2exp)
library(IsoPriceR)
library(ggplot2)
library(latex2exp)
library(kableExtra)
library(xtable)
setwd(dirname(rstudioapi::getActiveDocumentContext()$path))
getwd()
[1] "/Users/goffard/code/market_based_insurance_ratemaking/notebooks"

Let \(X\) be a random variable that represents the total health related over a given time period, say one year, associated to a pet. Our goal is to find the parameter \(\theta\) that best explains our market data made of insurance quotes \[ \tilde{p}_i = f_i\left[\mathbb{E}(g_i(X))\right],\text{ }i = 1,\ldots, n, \] where \(g_i\) are the coverage functions and \(f_i\) are the loading function. The coverage functions are known and of the form \[ g(x) = \min(\max(r\cdot x - d, 0), l), \] where \(r\) is the rate of coverage, \(d\) is the deductible and \(l\) is the limit.The loading functions are unknown and will be approximated by a generic function \(f\).

In the first section we consider the problem of finding \(\theta\) if we know access to the pure premiums

\[ p_i = \left[\mathbb{E}(g_i(X))\right],\text{ }i = 1,\ldots, n, \] which is not a realistic situation in practice. We then consider an isotonic regression model to approximate \(f\) and compares it to a parametric curve, linear withe \(f(x) = a + b\cdot x\) for simplicity. We consider an example where

\[ X = \sum_{k=1}^NU_k, \] where \(N\sim\text{Pois}(\lambda = 3)\) and the \(U_k\) are iid and lognormally distributed \(U\sim\text{LogNormal}(\mu =0 , \sigma = 1)\)

set.seed(142)

# Loss model
freq_dist <- model("poisson", c("lambda"))
sev_dist <- model("lognormal", c("mu", "sigma"))
l_m <- loss_model(freq_dist, sev_dist, "Poisson-Lognormal")

# True parameter
lambda_true <- 3; mu_true <- 0; sigma_true <- 1
true_parms <- t(as.matrix(c(lambda_true, mu_true, sigma_true)))
colnames(true_parms) <- l_m$parm_names

# Data
## Sample of claim data to calculate the true pure premium via Monte Carlo
X_true <- sample_X(l_m, true_parms, R = 10000)
summary(X_true)
       V1        
 Min.   : 0.000  
 1st Qu.: 1.705  
 Median : 3.765  
 Mean   : 4.946  
 3rd Qu.: 6.772  
 Max.   :76.830  

I - Generalized method of moments

If the pure premiums

\[ p_i = \mathbb{E}_(g_i(X)),\text{ }i = 1,\ldots, n, \]

are available then our estimation problem becomes find \(\theta\) that minimizes

\[ d\left[p_{1:n},p_{1:n}^\theta\right] = \sum_i w_i^{RMSE}(p_i -p_i^\theta)^2, \]

where \(p_i^\theta = \mathbb{E}_{\theta}(g_i(X))\). Identifiability holds if there exists a unique \(\theta^*\) such that

\[ \theta^* = \underset{\theta\in \Theta}{\text{argmin}}\, d\left[p_{1:n},p_{1:n}^\theta\right]. \]

This condition ressembles the global identification conditions of the Generalized Method of Moments which is typically hard to show. Existence of minimum requires that \(\theta\mapsto d\left[p_{1:n},p_{1:n}^\theta\right]\) is continuous and that \(\Theta\) is compact. Uniqueness would then hold if \(\theta\mapsto d\left[p_{1:n},p_{1:n}^\theta\right]\) is strictly convex. This condition is difficult to verify because an analytical expression of \(\theta\mapsto d\left[p_{1:n},p_{1:n}^\theta\right]\) is out of our reach and this for almost all the compound sum \(X\). A necessary condition is that the dimension of \(\theta\) is lower than \(n\) which is the number of moments we hold. Let us consider the case \(n = 3\) with

\[ g_1 = 0.85\cdot X\text{, } g_2 = \max(X - 1.8, 0) \text{, and }g_3 = \min(X, 6) \]

it is equivalent to the coverages \((r_1 = 0.85, d_1 = 0, l_1 = \infty)\), \((r_2 = 1, d_2 = 1.8, l_2 = \infty)\), and \((r_3 = 1, d_3 = 0, l_3 = 6)\).

## Characteristics of the pure premium which depends on the parameter of the insurance coverage
p_p <- pure_premium("xs", c("r", "d", "l"))
## Random premium parameters
n <- 3; r <- c(0.75, 1, 1); d <- c(0, 1.8, 0); l <- c(Inf, Inf, 6)

## Data frame with the insurance coverage parameters and the pure premium
th_premium <- matrix(c(r, d, l), nrow = n)
pps <- compute_pure_premia(X_true, coverage_type = "xs", th_premium)
sds <- sd_pure_premia(X_true, coverage_type = "xs", th_premium)
df_pp <- data.frame(r = r, d = d, l = l, pp=pps)
df_pp
latex_code <- xtable(df_pp)
print(latex_code, include.rownames = FALSE)
% latex table generated in R 4.4.2 by xtable 1.8-4 package
% Tue Jan 28 16:56:33 2025
\begin{table}[ht]
\centering
\begin{tabular}{rrrr}
  \hline
r & d & l & pp \\ 
  \hline
0.75 & 0.00 & Inf & 3.71 \\ 
  1.00 & 1.80 & Inf & 3.40 \\ 
  1.00 & 0.00 & 6.00 & 3.63 \\ 
   \hline
\end{tabular}
\end{table}

The pure premium computed above corresponds to a well specified case where there exists \(\theta^* = \theta_0\) for which

\[ d\left[p_{1:n},p_{1:n}^{\theta^{\ast}}\right] = 0 \] Note that we assign the same weight to each if the pre premium. We can check the uniqueness empirically, first in one dimension as we plot the functions \(\sigma\mapsto d\left[p_{1:n},p_{1:n}^{\theta^{\ast}}\right]\Big\rvert_{\mu =0, \lambda = 3}\), \(\mu\mapsto d\left[p_{1:n},p_{1:n}^{\theta^{\ast}}\right]\Big\rvert_{\sigma =1, \lambda = 3}\), and \(\lambda\mapsto d\left[p_{1:n},p_{1:n}^{\theta^{\ast}}\right]\Big\rvert_{\mu =0, \sigma = 1}\)

data <- df_pp[,c("r", "d", "l")]
data["x"] <- df_pp$pp
ths_premium <- as.matrix(data[p_p$parm_names])
lambda_true <- 3; mu_true <- 0; sigma_true <- 1

# Sigma --------------
# Pure premium calculation for a range of risk model parameters
sigmas <- seq(0.1, 2, 0.1)
cloud <- as.matrix(expand.grid(c(3), c(0),sigmas))
colnames(cloud) <- l_m$parm_names
X <- sample_X(l_m , cloud, R = 10000)
pp_fake <- compute_pure_premia(X, coverage_type = "xs", ths_premium)
                                                                     
# Calculation of the RMSE between true and wrong pure premium
ds <- sapply(1:nrow(cloud), function(k) mean((pp_fake[, k] - data$x)^2)^(1/2)) 
df_ds <- data.frame(cloud)
df_ds <- cbind(df_ds, ds)
(sig_pure_RMSE <- ggplot(data = df_ds) + geom_line(mapping = aes(x = sigma, y = ds)) +  ggplot2::theme_classic(base_size = 14) + labs(x = expression(sigma), title = "RMSE", y = "") )
ggsave("../figures/sig_pure_RMSE.pdf", sig_pure_RMSE)
Saving 7.29 x 4.51 in image

# Lambda --------------
lambdas <- seq(0.1, 5, 0.1)
cloud <- as.matrix(expand.grid(lambdas, c(0),c(1)))
colnames(cloud) <- l_m$parm_names
X <- sample_X(l_m , cloud, R = 10000)
pp_fake <- compute_pure_premia(X, coverage_type = "xs", ths_premium)
                                                                     
# Calculation of the RMSE between true and wrong pure premium
ds <- sapply(1:nrow(cloud), function(k) mean((pp_fake[, k] - data$x)^2)^(1/2)) 
df_ds <- data.frame(cloud)
df_ds <- cbind(df_ds, ds)
(lambda_pure_RMSE <- ggplot(data = df_ds) + geom_line(mapping = aes(x = lambda, y = ds)) +  ggplot2::theme_classic(base_size = 14) + labs(x = expression(lambda), title = "RMSE", y = "") )
ggsave("../figures/lambda_pure_RMSE.pdf", lambda_pure_RMSE)
Saving 7.29 x 4.51 in image

# mu --------------
mus <- seq(-3, 3, 0.1)
cloud <- as.matrix(expand.grid(c(3), mus,c(1)))
colnames(cloud) <- l_m$parm_names
X <- sample_X(l_m , cloud, R = 10000)
pp_fake <- compute_pure_premia(X, coverage_type = "xs", ths_premium)
                                                                     
# Calculation of the RMSE between true and wrong pure premium
ds <- sapply(1:nrow(cloud), function(k) mean((pp_fake[, k] - data$x)^2)^(1/2)) 
df_ds <- data.frame(cloud)
df_ds <- cbind(df_ds, ds)
(mu_pure_RMSE <- ggplot(data = df_ds) + geom_line(mapping = aes(x = mu, y = ds)) +  ggplot2::theme_classic(base_size = 14) + labs(x = expression(mu), title = "RMSE", y = "") )
ggsave("../figures/mu_pure_RMSE.pdf", mu_pure_RMSE)
Saving 7.29 x 4.51 in image

Each parameters can be identified separately, now of course identifying them jointly is another matter, there might exists several combinations of parameters that yield the same pure premium. Our optimization allows us to investigate by searching efficiently the parameter space.

We use the follwing prior assumptions: \[ \lambda\sim\text{Unif}(0, 10)\text{, }\mu\sim\text{Unif}(-3, 3)\text{, and }\sigma\sim\text{Unif}(0, 5) \] and the following settings for the algorithm

\[ J = 500\text{ and }R = 500 \] Recall that \(J\) is the population size of the clouds of particles and \(R\) is the number of Monte Carlo replications.

# Prior settings for the parameters
prior_lambda <- prior_dist("uniform", "lambda",c(0, 10))
prior_mu <- prior_dist("uniform", "mu",c(-3,3))
prior_sigma <- prior_dist("uniform", "sigma", c(0, 5))
prior_distributions <- list(prior_lambda, prior_mu, prior_sigma)
model_prior <- independent_priors(prior_distributions)

# Settings for the abc algorithm
popSize <- 500;  MC_R<- 500; acc <- 0.01; sp_bounds <- c(1, 1); eps_min <- 0.02

sum(sds/sqrt(MC_R)^2)
[1] 0.02037392
data <- df_pp[,c("r", "d", "l")]
data["x"] <- df_pp$pp
# res_abc <- abc(data, model_prior, l_m, p_p, popSize, MC_R, acc, sp_bounds, eps_min)
# save(res_abc, file = "../data/res_abc_supp_material_Section1.RData")
load("../data/res_abc_supp_material_Section1.RData")
# We retrieve the cloud of particles associated to each generation
cloud_dfs <- lapply(res_abc$Clouds, as.data.frame)
cloud_df <- do.call(rbind, cloud_dfs)
cloud_df$generation <- sort(rep(1:length(res_abc$Clouds), popSize))
cloud_df_plot <- rbind(
  data.frame(parm = cloud_df$lambda, parm_name = "lambda", generation = cloud_df$generation),
  data.frame(parm = cloud_df$sigma, parm_name = "sigma", generation = cloud_df$generation)
)
cloud_df_plot$parm_name <-as.factor(cloud_df_plot$parm_name) 
levels(cloud_df_plot$parm_name) <- c(lambda = TeX("$\\lambda$"), sigma = TeX("$\\sigma$"))

(posterior_lambda <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=lambda, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\lambda$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,1]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_lambda_supp_mat.pdf", posterior_lambda)
Saving 7.29 x 4.51 in image

(posterior_mu <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=mu, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\mu$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,2]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_mu_supp_mat.pdf", posterior_mu)
Saving 7.29 x 4.51 in image

(posterior_sig <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=sigma, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\sigma$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,3]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_sig_supp_mat.pdf", posterior_sig)
Saving 7.29 x 4.51 in image

II - Optimization based on the commercial premiums without regularization

We do not have access to the pure premium, instead we have the commercial premiums

\[ \tilde{p}_i = f_i\left[\mathbb{E}(g_i(X))\right],\text{ }i = 1,\ldots, n, \]

where the loading functions must be approximated. We therefore have to choose a model \(f\) such that

\[ \tilde{p}_i = f(p_{i}^\theta),\text{ }i = 1,\ldots, n \] and hope that there exists a unique \(\theta^*\) such that

\[ \theta^* = \underset{\theta\in \Theta}{\text{argmin}}\, d\left[\tilde{p}_{1:n},f(p_{1:n}^\theta)\right]. \] This typically depends on the choice for \(f\) we consider isotonic and linear. Isotonic regression impose monotonicity which is desirable since we believe it would make sense if

\[ p_i > p_j\Rightarrow \tilde{p}_i > \tilde{p}_j. \]

Furthermore isotonic regression is non parametric which mitigate the risk of misspecification. The drawback is overfitting as perfect interpolation of may occur in a small sample size setting.

Well-specified case

In our example we take the following loading fuunctions

\[ f_1(x) = 1.38\cdot x\text{, }f_2(x) = 1.1\cdot x \text{, and } f_3(x) = 1.4\cdot x \]

data <- df_pp[,c("r", "d", "l")]
data["x"] <- c(df_pp$pp[1]*1.38 , df_pp$pp[2]*1.1 , df_pp$pp[3] * 1.4 )
data$x;df_pp$pp
[1] 5.119139 3.742905 5.075182
[1] 3.709521 3.402641 3.625130
# data["x"] <- df_pp$pp
ths_premium <- as.matrix(data[p_p$parm_names])
lambda_true <- 3; mu_true <- 0; sigma_true <- 1
res_iso <- isoreg(df_pp$pp, data$x)

(pp_cp_well_specified <- ggplot() + geom_point(mapping = aes(x = df_pp$pp, y = data$x)) + geom_step(mapping = aes(x= df_pp$pp, y = data$x)) + ggplot2::theme_classic(base_size = 14) + labs(x = "Pure Premium", title = "Commercial premium", y = "") )
ggsave("../figures/pp_cp_well_specified.pdf", pp_cp_well_specified)
Saving 7.29 x 4.51 in image

# Sigma --------------
# Pure premium calculation for a range of risk model parameters
sigmas <- seq(0.1, 2, 0.02)
cloud <- as.matrix(expand.grid(c(3), c(0),sigmas))
colnames(cloud) <- l_m$parm_names
X <- sample_X(l_m , cloud, R = 10000)
pp_fake <- compute_pure_premia(X, coverage_type = "xs", ths_premium)
# Calculation of the RMSE between true and wrong commercial premium
ress_iso <- lapply(1:ncol(pp_fake), function(k) isoreg(pp_fake[, k], data$x))

ress_lm <- sapply(1:ncol(pp_fake), function(k) as.numeric(lm(data$x~pp_fake[, k] - 1)$coefficients))
ress_lm[sigmas == 1]
[1] 1.290995
as.numeric(lm(data$x ~ pp_fake[, sigmas == 1] - 1)$coefficients)
[1] 1.290995
mean((mean((pp_fake[, sigmas == 1] * ress_lm[sigmas == 1] - data$x)^2)^(1/2) - data$x)^2)^(1/2)
[1] 4.215107
fs_iso <- lapply(ress_iso, function(res_iso) stepfun(sort(res_iso$x), c(min(res_iso$yf) /2 ,
                                                                      res_iso$yf), f = 1))
ds_iso <- sapply(1:nrow(cloud), function(k) mean((fs_iso[[k]](pp_fake[, k]) - data$x)^2)^(1/2)) 
ds_lm <- sapply(1:nrow(cloud), function(k) mean((pp_fake[, k] / ress_lm[k] - data$x)^2)^(1/2)) 

df_ds <- data.frame(cloud)
df_ds <- cbind(df_ds, ds_iso)
df_ds <- cbind(df_ds, ds_lm)
(rmse_sigma_iso <- ggplot(data = df_ds) + geom_line(mapping = aes(x = sigma, y = ds_iso)) + ggplot2::theme_classic(base_size = 14) + labs(x = expression(sigma), title = "RMSE", y = "") )
ggsave("../figures/rmse_sigma_iso.pdf", rmse_sigma_iso)
Saving 7.29 x 4.51 in image

(rmse_sigma_linear <- ggplot(data = df_ds) + geom_line(mapping = aes(x = sigma, y = ds_lm)) + ggplot2::theme_classic(base_size = 14) + labs(x = expression(sigma), title = "RMSE", y = ""))
ggsave("../figures/rmse_sigma_linear.pdf", rmse_sigma_linear)
Saving 7.29 x 4.51 in image

# Prior settings for the parameters
prior_lambda <- prior_dist("constant", "lambda",c(3))
prior_mu <- prior_dist("constant", "mu",c(0))
prior_sigma <- prior_dist("uniform", "sigma", c(0, 2))
prior_distributions <- list(prior_lambda, prior_mu, prior_sigma)
model_prior <- independent_priors(prior_distributions)

# Settings for the abc algorithm
popSize <- 2000;  MC_R<- 500; acc <- 0.01; sp_bounds <- c(10^(-16), Inf); eps_min <- 10^-8
res_abc <- abc(data, model_prior, l_m, p_p, popSize, MC_R, acc, sp_bounds, eps_min)
[1] "Sampling generation of particles  1 ; eps =  Inf ; ESS =  2000"
[1] "Sampling generation of particles  2 ; eps =  0.258092889599686 ; ESS =  1001"
=======================================================================================================================================================================================================================================
[1] "Sampling generation of particles  3 ; eps =  0.00110036424142931 ; ESS =  1000.32220779949"
cloud_dfs <- lapply(res_abc$Clouds, as.data.frame)
cloud_df <- do.call(rbind, cloud_dfs)
cloud_df$generation <- sort(rep(1:length(res_abc$Clouds), popSize))
cloud_df_plot <- data.frame(parm = cloud_df$sigma, parm_name = "sigma", generation = cloud_df$generation)

cloud_df_plot$parm_name <-as.factor(cloud_df_plot$parm_name) 
levels(cloud_df_plot$parm_name) <- c(sigma = TeX("$\\sigma$"))
(posterior_sig_iso <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=sigma, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\sigma$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,3]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_sig_iso_supp_mat.pdf", posterior_sig_iso)
Saving 7.29 x 4.51 in image

# Prior settings for the parameters
prior_lambda <- prior_dist("uniform", "lambda",c(0, 5))
prior_mu <- prior_dist("uniform", "mu",c(-3,3))
prior_sigma <- prior_dist("uniform", "sigma", c(0, 2))
prior_distributions <- list(prior_lambda, prior_mu, prior_sigma)
model_prior <- independent_priors(prior_distributions)

# Settings for the abc algorithm
popSize <- 500;  MC_R<- 500; acc <- 0.01; sp_bounds <- c(10^(-16), Inf); eps_min <- 10^(-8)
# res_abc <- abc(data, model_prior, l_m, p_p, popSize, MC_R, acc, sp_bounds, eps_min)
# save(res_abc, file = "../data/res_abc_wo_reg_supp_material_Section2.RData")
load("../data/res_abc_wo_reg_supp_material_Section2.RData")
# We retrieve the cloud of particles associated to each generation
cloud_dfs <- lapply(res_abc$Clouds, as.data.frame)
cloud_df <- do.call(rbind, cloud_dfs)
cloud_df$generation <- sort(rep(1:length(res_abc$Clouds), popSize))
cloud_df_plot <- rbind(
  data.frame(parm = cloud_df$lambda, parm_name = "lambda", generation = cloud_df$generation),
    data.frame(parm = cloud_df$sigma, parm_name = "mu", generation = cloud_df$generation),
  data.frame(parm = cloud_df$sigma, parm_name = "sigma", generation = cloud_df$generation)
)
cloud_df_plot$parm_name <-as.factor(cloud_df_plot$parm_name) 
levels(cloud_df_plot$parm_name) <- c(lambda = TeX("$\\lambda$"), mu = TeX("$\\mu$"),  sigma = TeX("$\\sigma$"))

(posterior_lambda <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=lambda, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\lambda$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,1]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_lambda_wo_reg_supp_mat.pdf", posterior_lambda)
Saving 7.29 x 4.51 in image

(posterior_mu <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=mu, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\mu$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,2]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_mu_wo_reg_supp_mat.pdf", posterior_mu)
Saving 7.29 x 4.51 in image

(posterior_sig <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=sigma, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\sigma$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,3]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_sig_wo_reg_supp_mat.pdf", posterior_sig)
Saving 7.29 x 4.51 in image

# Prior settings for the parameters
prior_lambda <- prior_dist("uniform", "lambda",c(0, 5))
prior_mu <- prior_dist("uniform", "mu",c(-3,3))
prior_sigma <- prior_dist("uniform", "sigma", c(0, 2))
prior_distributions <- list(prior_lambda, prior_mu, prior_sigma)
model_prior <- independent_priors(prior_distributions)

# Settings for the abc algorithm
popSize <- 500;  MC_R<- 500; acc <- 0.001; sp_bounds <- c(min(df_pp$pp / data$x), max(df_pp$pp / data$x))
eps_min <- 0.0001
# res_abc <- abc(data, model_prior, l_m, p_p, popSize, MC_R, acc, sp_bounds, eps_min)
# save(res_abc, file = "../data/res_abc_w_reg_supp_material_Section2.RData")
load("../data/res_abc_w_reg_supp_material_Section2.RData")

# We retrieve the cloud of particles associated to each generation
cloud_dfs <- lapply(res_abc$Clouds, as.data.frame)
cloud_df <- do.call(rbind, cloud_dfs)
cloud_df$generation <- sort(rep(1:length(res_abc$Clouds), popSize))
cloud_df_plot <- rbind(
  data.frame(parm = cloud_df$lambda, parm_name = "lambda", generation = cloud_df$generation),
    data.frame(parm = cloud_df$sigma, parm_name = "mu", generation = cloud_df$generation),
  data.frame(parm = cloud_df$sigma, parm_name = "sigma", generation = cloud_df$generation)
)
cloud_df_plot$parm_name <-as.factor(cloud_df_plot$parm_name) 
levels(cloud_df_plot$parm_name) <- c(lambda = TeX("$\\lambda$"), mu = TeX("$\\mu$"),  sigma = TeX("$\\sigma$"))

(posterior_lambda <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=lambda, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\lambda$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,1]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_lambda_w_reg_supp_mat.pdf", posterior_lambda)
Saving 7.29 x 4.51 in image

(posterior_mu <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=mu, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\mu$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,2]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_mu_w_reg_supp_mat.pdf", posterior_mu)
Saving 7.29 x 4.51 in image

(posterior_sig <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=sigma, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\sigma$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,3]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_sig_w_reg_supp_mat.pdf", posterior_sig)
Saving 7.29 x 4.51 in image

Miss-specified case

In our example we take the following loading fuunctions

\[ f_1(x) = 1.1\cdot x\text{, }f_2(x) = 1.2\cdot x \text{, and } f_3(x) = 1.3\cdot x \]

data <- df_pp[,c("r", "d", "l")]
data["x"] <- c(df_pp$pp[1]*1.1 , df_pp$pp[2]*1.2 , df_pp$pp[3] * 1.3 )
# data["x"] <- df_pp$pp
ths_premium <- as.matrix(data[p_p$parm_names])
lambda_true <- 3; mu_true <- 0; sigma_true <- 1
res_iso <- isoreg(df_pp$pp, data$x)

(pp_cp_miss_specified <- ggplot() + geom_point(mapping = aes(x = df_pp$pp, y = data$x)) + geom_step(mapping = aes(x= df_pp$pp[order(df_pp$pp)], y = res_iso$yf)) + ggplot2::theme_classic(base_size = 14) + labs(x = "Pure Premium", title = "Commercial premium", y = "") )
ggsave("../figures/pp_cp_miss_specified.pdf", pp_cp_miss_specified)
Saving 7.29 x 4.51 in image

res_iso <- isoreg(df_pp$pp, data$x)
# Sigma --------------
# Pure premium calculation for a range of risk model parameters
sigmas <- seq(0.1, 2, 0.02)
cloud <- as.matrix(expand.grid(c(3), c(0),sigmas))
colnames(cloud) <- l_m$parm_names
X <- sample_X(l_m , cloud, R = 10000)
pp_fake <- compute_pure_premia(X, coverage_type = "xs", ths_premium)
# Calculation of the RMSE between true and wrong commercial premium
ress_iso <- lapply(1:ncol(pp_fake), function(k) isoreg(pp_fake[, k], data$x))

ress_lm <- sapply(1:ncol(pp_fake), function(k) as.numeric(lm(data$x~pp_fake[, k] - 1)$coefficients))
ress_lm[sigmas == 1]
[1] 1.193516
as.numeric(lm(data$x ~ pp_fake[, sigmas == 1] - 1)$coefficients)
[1] 1.193516
mean((mean((pp_fake[, sigmas == 1] * ress_lm[sigmas == 1] - data$x)^2)^(1/2) - data$x)^2)^(1/2)
[1] 4.003783
fs_iso <- lapply(ress_iso, function(res_iso) stepfun(sort(res_iso$x), c(min(res_iso$yf) /2 ,
                                                                      res_iso$yf), f = 1))
ds_iso <- sapply(1:nrow(cloud), function(k) mean((fs_iso[[k]](pp_fake[, k]) - data$x)^2)^(1/2)) 
ds_lm <- sapply(1:nrow(cloud), function(k) mean((pp_fake[, k] / ress_lm[k] - data$x)^2)^(1/2)) 

df_ds <- data.frame(cloud)
df_ds <- cbind(df_ds, ds_iso)
df_ds <- cbind(df_ds, ds_lm)
(rmse_sigma_iso <- ggplot(data = df_ds) + geom_line(mapping = aes(x = sigma, y = ds_iso)) + ggplot2::theme_classic(base_size = 14) + labs(x = expression(sigma), title = "RMSE", y = "") )
ggsave("../figures/rmse_sigma_iso_miss.pdf", rmse_sigma_iso)
Saving 7.29 x 4.51 in image

(rmse_sigma_linear <- ggplot(data = df_ds) + geom_line(mapping = aes(x = sigma, y = ds_lm)) + ggplot2::theme_classic(base_size = 14) + labs(x = expression(sigma), title = "RMSE", y = ""))
ggsave("../figures/rmse_sigma_linear_miss.pdf", rmse_sigma_linear)
Saving 7.29 x 4.51 in image

# Prior settings for the parameters
prior_lambda <- prior_dist("uniform", "lambda",c(0,5))
prior_mu <- prior_dist("uniform", "mu",c(-3,3))
prior_sigma <- prior_dist("uniform", "sigma", c(0, 2))
prior_distributions <- list(prior_lambda, prior_mu, prior_sigma)
model_prior <- independent_priors(prior_distributions)

# Settings for the abc algorithm
popSize <- 500;  MC_R<- 500; acc <- 0.01; sp_bounds <- c(10^(-16), Inf); eps_min <- 0.0001
# res_abc <- abc(data, model_prior, l_m, p_p, popSize, MC_R, acc, sp_bounds, eps_min)
# save(res_abc, file = "../data/res_abc_wo_reg_miss_supp_material_Section2.RData")
load("../data/res_abc_wo_reg_miss_supp_material_Section2.RData")

# We retrieve the cloud of particles associated to each generation
cloud_dfs <- lapply(res_abc$Clouds, as.data.frame)
cloud_df <- do.call(rbind, cloud_dfs)
cloud_df$generation <- sort(rep(1:length(res_abc$Clouds), popSize))
cloud_df_plot <- rbind(
  data.frame(parm = cloud_df$lambda, parm_name = "lambda", generation = cloud_df$generation),
    data.frame(parm = cloud_df$sigma, parm_name = "mu", generation = cloud_df$generation),
  data.frame(parm = cloud_df$sigma, parm_name = "sigma", generation = cloud_df$generation)
)
cloud_df_plot$parm_name <-as.factor(cloud_df_plot$parm_name) 
levels(cloud_df_plot$parm_name) <- c(lambda = TeX("$\\lambda$"), mu = TeX("$\\mu$"),  sigma = TeX("$\\sigma$"))

(posterior_lambda <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=lambda, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\lambda$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,1]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_lambda_wo_reg_miss_supp_mat.pdf", posterior_lambda)
Saving 7.29 x 4.51 in image

(posterior_mu <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=mu, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\mu$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,2]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_mu_wo_reg_miss_supp_mat.pdf", posterior_mu)
Saving 7.29 x 4.51 in image

(posterior_sig <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=sigma, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\sigma$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,3]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_sig_wo_reg_miss_supp_mat.pdf", posterior_sig)
Saving 7.29 x 4.51 in image

# Prior settings for the parameters
prior_lambda <- prior_dist("uniform", "lambda",c(0, 5))
prior_mu <- prior_dist("uniform", "mu",c(-3,3))
prior_sigma <- prior_dist("uniform", "sigma", c(0, 2))
prior_distributions <- list(prior_lambda, prior_mu, prior_sigma)
model_prior <- independent_priors(prior_distributions)

# Settings for the abc algorithm
popSize <- 500;  MC_R<- 500; acc <- 0.001; sp_bounds <- c(min(df_pp$pp / data$x), max(df_pp$pp / data$x))
eps_min <- 0.01
# res_abc <- abc(data, model_prior, l_m, p_p, popSize, MC_R, acc, sp_bounds, eps_min)
# save(res_abc, file = "../data/res_abc_w_reg_miss_supp_material_Section2.RData")
load("../data/res_abc_w_reg_miss_supp_material_Section2.RData")
# We retrieve the cloud of particles associated to each generation
cloud_dfs <- lapply(res_abc$Clouds, as.data.frame)
cloud_df <- do.call(rbind, cloud_dfs)
cloud_df$generation <- sort(rep(1:length(res_abc$Clouds), popSize))
cloud_df_plot <- rbind(
  data.frame(parm = cloud_df$lambda, parm_name = "lambda", generation = cloud_df$generation),
    data.frame(parm = cloud_df$sigma, parm_name = "mu", generation = cloud_df$generation),
  data.frame(parm = cloud_df$sigma, parm_name = "sigma", generation = cloud_df$generation)
)
cloud_df_plot$parm_name <-as.factor(cloud_df_plot$parm_name) 
levels(cloud_df_plot$parm_name) <- c(lambda = TeX("$\\lambda$"), mu = TeX("$\\mu$"),  sigma = TeX("$\\sigma$"))

(posterior_lambda <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=lambda, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\lambda$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,1]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_lambda_w_reg_miss_supp_mat.pdf", posterior_lambda)
Saving 7.29 x 4.51 in image

(posterior_mu <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=mu, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\mu$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,2]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_mu_w_reg_miss_supp_mat.pdf", posterior_mu)
Saving 7.29 x 4.51 in image

(posterior_sig <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=sigma, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\sigma$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,3]), color="black",
                                   linetype="dashed"))
ggsave("../figures/posterior_sig_w_reg_miss_supp_mat.pdf", posterior_sig)
Saving 7.29 x 4.51 in image

(posterior_lambda <- ggplot2::ggplot(data = cloud_df ) + ggplot2::geom_density(data = cloud_df, mapping = aes(x=lambda, color = as.factor(generation))) + ggplot2::labs(x=TeX("$\\lambda$"), y = "Density", color = "Generation") + ggplot2::theme_classic(base_size = 20) + ggplot2::theme(legend.position="right") + ggplot2::geom_vline(ggplot2::aes(xintercept=true_parms[,1]), color="black",
                                   linetype="dashed"))

LS0tCnRpdGxlOiAiU3VwbGVtZW50YXJ5IG1hdGVyaWFsOiBHZW5lcmFsaXplZCBtZXRob2Qgb2YgbW9tZW50cyBhbmQgaWRlbnRpZmlhYmlsaXR5IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgoKYGBge3J9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKbGlicmFyeShsYXRleDJleHApCmxpYnJhcnkoSXNvUHJpY2VSKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobGF0ZXgyZXhwKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoeHRhYmxlKQpzZXR3ZChkaXJuYW1lKHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGgpKQpnZXR3ZCgpCmBgYApMZXQgJFgkIGJlIGEgcmFuZG9tIHZhcmlhYmxlIHRoYXQgcmVwcmVzZW50cyB0aGUgdG90YWwgaGVhbHRoIHJlbGF0ZWQgb3ZlciBhIGdpdmVuIHRpbWUgcGVyaW9kLCBzYXkgb25lIHllYXIsIGFzc29jaWF0ZWQgdG8gYSBwZXQuIE91ciBnb2FsIGlzIHRvIGZpbmQgdGhlIHBhcmFtZXRlciAkXHRoZXRhJCB0aGF0IGJlc3QgZXhwbGFpbnMgb3VyIG1hcmtldCBkYXRhIG1hZGUgb2YgaW5zdXJhbmNlIHF1b3RlcwokJApcdGlsZGV7cH1faSA9IGZfaVxsZWZ0W1xtYXRoYmJ7RX0oZ19pKFgpKVxyaWdodF0sXHRleHR7IH1pID0gMSxcbGRvdHMsIG4sCiQkCndoZXJlICRnX2kkIGFyZSB0aGUgY292ZXJhZ2UgZnVuY3Rpb25zIGFuZCAkZl9pJCBhcmUgdGhlIGxvYWRpbmcgZnVuY3Rpb24uIFRoZSBjb3ZlcmFnZSBmdW5jdGlvbnMgYXJlIGtub3duIGFuZCBvZiB0aGUgZm9ybQokJApnKHgpID0gXG1pbihcbWF4KHJcY2RvdCB4IC0gZCwgMCksIGwpLAokJAp3aGVyZSAkciQgaXMgdGhlIHJhdGUgb2YgY292ZXJhZ2UsICRkJCBpcyB0aGUgZGVkdWN0aWJsZSBhbmQgJGwkIGlzIHRoZSBsaW1pdC5UaGUgbG9hZGluZyBmdW5jdGlvbnMgYXJlIHVua25vd24gYW5kIHdpbGwgYmUgYXBwcm94aW1hdGVkIGJ5IGEgZ2VuZXJpYyBmdW5jdGlvbiAkZiQuIAoKSW4gdGhlIGZpcnN0IHNlY3Rpb24gd2UgY29uc2lkZXIgdGhlIHByb2JsZW0gb2YgZmluZGluZyAkXHRoZXRhJCBpZiB3ZSBrbm93IGFjY2VzcyB0byB0aGUgcHVyZSBwcmVtaXVtcyAKCiQkCnBfaSA9IFxsZWZ0W1xtYXRoYmJ7RX0oZ19pKFgpKVxyaWdodF0sXHRleHR7IH1pID0gMSxcbGRvdHMsIG4sCiQkCndoaWNoIGlzIG5vdCBhIHJlYWxpc3RpYyBzaXR1YXRpb24gaW4gcHJhY3RpY2UuIFdlIHRoZW4gY29uc2lkZXIgYW4gaXNvdG9uaWMgcmVncmVzc2lvbiBtb2RlbCB0byBhcHByb3hpbWF0ZSAkZiQgYW5kIGNvbXBhcmVzIGl0IHRvIGEgcGFyYW1ldHJpYyBjdXJ2ZSwgbGluZWFyIHdpdGhlICRmKHgpID0gYSArIGJcY2RvdCB4JCBmb3Igc2ltcGxpY2l0eS4gV2UgY29uc2lkZXIgYW4gZXhhbXBsZSB3aGVyZQoKJCQKWCA9IFxzdW1fe2s9MX1eTlVfaywKJCQKd2hlcmUgJE5cc2ltXHRleHR7UG9pc30oXGxhbWJkYSA9IDMpJCBhbmQgdGhlICRVX2skIGFyZSBpaWQgYW5kIGxvZ25vcm1hbGx5IGRpc3RyaWJ1dGVkICRVXHNpbVx0ZXh0e0xvZ05vcm1hbH0oXG11ID0wICwgXHNpZ21hID0gMSkkCgpgYGB7cn0Kc2V0LnNlZWQoMTQyKQoKIyBMb3NzIG1vZGVsCmZyZXFfZGlzdCA8LSBtb2RlbCgicG9pc3NvbiIsIGMoImxhbWJkYSIpKQpzZXZfZGlzdCA8LSBtb2RlbCgibG9nbm9ybWFsIiwgYygibXUiLCAic2lnbWEiKSkKbF9tIDwtIGxvc3NfbW9kZWwoZnJlcV9kaXN0LCBzZXZfZGlzdCwgIlBvaXNzb24tTG9nbm9ybWFsIikKCiMgVHJ1ZSBwYXJhbWV0ZXIKbGFtYmRhX3RydWUgPC0gMzsgbXVfdHJ1ZSA8LSAwOyBzaWdtYV90cnVlIDwtIDEKdHJ1ZV9wYXJtcyA8LSB0KGFzLm1hdHJpeChjKGxhbWJkYV90cnVlLCBtdV90cnVlLCBzaWdtYV90cnVlKSkpCmNvbG5hbWVzKHRydWVfcGFybXMpIDwtIGxfbSRwYXJtX25hbWVzCgojIERhdGEKIyMgU2FtcGxlIG9mIGNsYWltIGRhdGEgdG8gY2FsY3VsYXRlIHRoZSB0cnVlIHB1cmUgcHJlbWl1bSB2aWEgTW9udGUgQ2FybG8KWF90cnVlIDwtIHNhbXBsZV9YKGxfbSwgdHJ1ZV9wYXJtcywgUiA9IDEwMDAwKQpzdW1tYXJ5KFhfdHJ1ZSkKYGBgCgoKIyBJIC0gR2VuZXJhbGl6ZWQgbWV0aG9kIG9mIG1vbWVudHMKCklmIHRoZSBwdXJlIHByZW1pdW1zIAoKJCQKcF9pID0gXG1hdGhiYntFfV8oZ19pKFgpKSxcdGV4dHsgfWkgPSAxLFxsZG90cywgbiwKJCQKCmFyZSBhdmFpbGFibGUgdGhlbiBvdXIgZXN0aW1hdGlvbiBwcm9ibGVtIGJlY29tZXMgZmluZCAkXHRoZXRhJCB0aGF0IG1pbmltaXplcyAKCiQkCmRcbGVmdFtwX3sxOm59LHBfezE6bn1eXHRoZXRhXHJpZ2h0XSA9IFxzdW1faSB3X2lee1JNU0V9KHBfaSAtcF9pXlx0aGV0YSleMiwKJCQKCndoZXJlICRwX2leXHRoZXRhID0gXG1hdGhiYntFfV97XHRoZXRhfShnX2koWCkpJC4gSWRlbnRpZmlhYmlsaXR5IGhvbGRzIGlmIHRoZXJlIGV4aXN0cyBhIHVuaXF1ZSAkXHRoZXRhXiokIHN1Y2ggdGhhdCAKCiQkClx0aGV0YV4qID0gXHVuZGVyc2V0e1x0aGV0YVxpbiBcVGhldGF9e1x0ZXh0e2FyZ21pbn19XCwgZFxsZWZ0W3BfezE6bn0scF97MTpufV5cdGhldGFccmlnaHRdLgokJAoKVGhpcyBjb25kaXRpb24gcmVzc2VtYmxlcyB0aGUgZ2xvYmFsIGlkZW50aWZpY2F0aW9uIGNvbmRpdGlvbnMgb2YgdGhlIEdlbmVyYWxpemVkIE1ldGhvZCBvZiBNb21lbnRzIHdoaWNoIGlzIHR5cGljYWxseSBoYXJkIHRvIHNob3cuIEV4aXN0ZW5jZSBvZiBtaW5pbXVtIHJlcXVpcmVzIHRoYXQgJFx0aGV0YVxtYXBzdG8gZFxsZWZ0W3BfezE6bn0scF97MTpufV5cdGhldGFccmlnaHRdJCBpcyBjb250aW51b3VzIGFuZCB0aGF0ICRcVGhldGEkIGlzIGNvbXBhY3QuIFVuaXF1ZW5lc3Mgd291bGQgdGhlbiBob2xkIGlmICRcdGhldGFcbWFwc3RvIGRcbGVmdFtwX3sxOm59LHBfezE6bn1eXHRoZXRhXHJpZ2h0XSQgaXMgc3RyaWN0bHkgY29udmV4LiBUaGlzIGNvbmRpdGlvbiBpcyBkaWZmaWN1bHQgdG8gdmVyaWZ5IGJlY2F1c2UgYW4gYW5hbHl0aWNhbCBleHByZXNzaW9uIG9mICRcdGhldGFcbWFwc3RvIGRcbGVmdFtwX3sxOm59LHBfezE6bn1eXHRoZXRhXHJpZ2h0XSQgaXMgb3V0IG9mIG91ciByZWFjaCBhbmQgdGhpcyBmb3IgYWxtb3N0IGFsbCB0aGUgY29tcG91bmQgc3VtICRYJC4gQSBuZWNlc3NhcnkgY29uZGl0aW9uIGlzIHRoYXQgdGhlIGRpbWVuc2lvbiBvZiAkXHRoZXRhJCBpcyBsb3dlciB0aGFuICRuJCB3aGljaCBpcyB0aGUgbnVtYmVyIG9mIG1vbWVudHMgd2UgaG9sZC4gTGV0IHVzIGNvbnNpZGVyIHRoZSBjYXNlICRuID0gMyQgd2l0aAoKJCQKZ18xID0gMC44NVxjZG90IFhcdGV4dHssIH0gZ18yID0gXG1heChYIC0gMS44LCAwKSBcdGV4dHssIGFuZCB9Z18zID0gXG1pbihYLCA2KQokJAoKaXQgaXMgZXF1aXZhbGVudCB0byB0aGUgY292ZXJhZ2VzICQocl8xID0gMC44NSwgZF8xID0gMCwgbF8xID0gXGluZnR5KSQsICQocl8yID0gMSwgZF8yID0gMS44LCBsXzIgPSBcaW5mdHkpJCwgYW5kICQocl8zID0gMSwgZF8zID0gMCwgbF8zID0gNikkLiAKCmBgYHtyfQojIyBDaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIHB1cmUgcHJlbWl1bSB3aGljaCBkZXBlbmRzIG9uIHRoZSBwYXJhbWV0ZXIgb2YgdGhlIGluc3VyYW5jZSBjb3ZlcmFnZQpwX3AgPC0gcHVyZV9wcmVtaXVtKCJ4cyIsIGMoInIiLCAiZCIsICJsIikpCiMjIFJhbmRvbSBwcmVtaXVtIHBhcmFtZXRlcnMKbiA8LSAzOyByIDwtIGMoMC43NSwgMSwgMSk7IGQgPC0gYygwLCAxLjgsIDApOyBsIDwtIGMoSW5mLCBJbmYsIDYpCgojIyBEYXRhIGZyYW1lIHdpdGggdGhlIGluc3VyYW5jZSBjb3ZlcmFnZSBwYXJhbWV0ZXJzIGFuZCB0aGUgcHVyZSBwcmVtaXVtCnRoX3ByZW1pdW0gPC0gbWF0cml4KGMociwgZCwgbCksIG5yb3cgPSBuKQpwcHMgPC0gY29tcHV0ZV9wdXJlX3ByZW1pYShYX3RydWUsIGNvdmVyYWdlX3R5cGUgPSAieHMiLCB0aF9wcmVtaXVtKQpzZHMgPC0gc2RfcHVyZV9wcmVtaWEoWF90cnVlLCBjb3ZlcmFnZV90eXBlID0gInhzIiwgdGhfcHJlbWl1bSkKZGZfcHAgPC0gZGF0YS5mcmFtZShyID0gciwgZCA9IGQsIGwgPSBsLCBwcD1wcHMpCmRmX3BwCmxhdGV4X2NvZGUgPC0geHRhYmxlKGRmX3BwKQpwcmludChsYXRleF9jb2RlLCBpbmNsdWRlLnJvd25hbWVzID0gRkFMU0UpCmBgYApUaGUgcHVyZSBwcmVtaXVtIGNvbXB1dGVkIGFib3ZlIGNvcnJlc3BvbmRzIHRvIGEgd2VsbCBzcGVjaWZpZWQgY2FzZSB3aGVyZSB0aGVyZSBleGlzdHMgJFx0aGV0YV4qID0gXHRoZXRhXzAkIGZvciB3aGljaCAKCiQkCmRcbGVmdFtwX3sxOm59LHBfezE6bn1ee1x0aGV0YV57XGFzdH19XHJpZ2h0XSA9IDAKJCQKTm90ZSB0aGF0IHdlIGFzc2lnbiB0aGUgc2FtZSB3ZWlnaHQgdG8gZWFjaCBpZiB0aGUgcHJlIHByZW1pdW0uIFdlIGNhbiBjaGVjayB0aGUgdW5pcXVlbmVzcyBlbXBpcmljYWxseSwgZmlyc3QgaW4gb25lIGRpbWVuc2lvbiBhcyB3ZSBwbG90IHRoZSBmdW5jdGlvbnMgJFxzaWdtYVxtYXBzdG8gZFxsZWZ0W3BfezE6bn0scF97MTpufV57XHRoZXRhXntcYXN0fX1ccmlnaHRdXEJpZ1xydmVydF97XG11ID0wLCBcbGFtYmRhID0gM30kLCAkXG11XG1hcHN0byBkXGxlZnRbcF97MTpufSxwX3sxOm59XntcdGhldGFee1xhc3R9fVxyaWdodF1cQmlnXHJ2ZXJ0X3tcc2lnbWEgPTEsIFxsYW1iZGEgPSAzfSQsIGFuZCAkXGxhbWJkYVxtYXBzdG8gZFxsZWZ0W3BfezE6bn0scF97MTpufV57XHRoZXRhXntcYXN0fX1ccmlnaHRdXEJpZ1xydmVydF97XG11ID0wLCBcc2lnbWEgPSAxfSQgIAoKCmBgYHtyfQpkYXRhIDwtIGRmX3BwWyxjKCJyIiwgImQiLCAibCIpXQpkYXRhWyJ4Il0gPC0gZGZfcHAkcHAKdGhzX3ByZW1pdW0gPC0gYXMubWF0cml4KGRhdGFbcF9wJHBhcm1fbmFtZXNdKQpsYW1iZGFfdHJ1ZSA8LSAzOyBtdV90cnVlIDwtIDA7IHNpZ21hX3RydWUgPC0gMQoKIyBTaWdtYSAtLS0tLS0tLS0tLS0tLQojIFB1cmUgcHJlbWl1bSBjYWxjdWxhdGlvbiBmb3IgYSByYW5nZSBvZiByaXNrIG1vZGVsIHBhcmFtZXRlcnMKc2lnbWFzIDwtIHNlcSgwLjEsIDIsIDAuMSkKY2xvdWQgPC0gYXMubWF0cml4KGV4cGFuZC5ncmlkKGMoMyksIGMoMCksc2lnbWFzKSkKY29sbmFtZXMoY2xvdWQpIDwtIGxfbSRwYXJtX25hbWVzClggPC0gc2FtcGxlX1gobF9tICwgY2xvdWQsIFIgPSAxMDAwMCkKcHBfZmFrZSA8LSBjb21wdXRlX3B1cmVfcHJlbWlhKFgsIGNvdmVyYWdlX3R5cGUgPSAieHMiLCB0aHNfcHJlbWl1bSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiMgQ2FsY3VsYXRpb24gb2YgdGhlIFJNU0UgYmV0d2VlbiB0cnVlIGFuZCB3cm9uZyBwdXJlIHByZW1pdW0KZHMgPC0gc2FwcGx5KDE6bnJvdyhjbG91ZCksIGZ1bmN0aW9uKGspIG1lYW4oKHBwX2Zha2VbLCBrXSAtIGRhdGEkeCleMileKDEvMikpIApkZl9kcyA8LSBkYXRhLmZyYW1lKGNsb3VkKQpkZl9kcyA8LSBjYmluZChkZl9kcywgZHMpCihzaWdfcHVyZV9STVNFIDwtIGdncGxvdChkYXRhID0gZGZfZHMpICsgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoeCA9IHNpZ21hLCB5ID0gZHMpKSArICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE0KSArIGxhYnMoeCA9IGV4cHJlc3Npb24oc2lnbWEpLCB0aXRsZSA9ICJSTVNFIiwgeSA9ICIiKSApCmdnc2F2ZSgiLi4vZmlndXJlcy9zaWdfcHVyZV9STVNFLnBkZiIsIHNpZ19wdXJlX1JNU0UpCgojIExhbWJkYSAtLS0tLS0tLS0tLS0tLQpsYW1iZGFzIDwtIHNlcSgwLjEsIDUsIDAuMSkKY2xvdWQgPC0gYXMubWF0cml4KGV4cGFuZC5ncmlkKGxhbWJkYXMsIGMoMCksYygxKSkpCmNvbG5hbWVzKGNsb3VkKSA8LSBsX20kcGFybV9uYW1lcwpYIDwtIHNhbXBsZV9YKGxfbSAsIGNsb3VkLCBSID0gMTAwMDApCnBwX2Zha2UgPC0gY29tcHV0ZV9wdXJlX3ByZW1pYShYLCBjb3ZlcmFnZV90eXBlID0gInhzIiwgdGhzX3ByZW1pdW0pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAojIENhbGN1bGF0aW9uIG9mIHRoZSBSTVNFIGJldHdlZW4gdHJ1ZSBhbmQgd3JvbmcgcHVyZSBwcmVtaXVtCmRzIDwtIHNhcHBseSgxOm5yb3coY2xvdWQpLCBmdW5jdGlvbihrKSBtZWFuKChwcF9mYWtlWywga10gLSBkYXRhJHgpXjIpXigxLzIpKSAKZGZfZHMgPC0gZGF0YS5mcmFtZShjbG91ZCkKZGZfZHMgPC0gY2JpbmQoZGZfZHMsIGRzKQoobGFtYmRhX3B1cmVfUk1TRSA8LSBnZ3Bsb3QoZGF0YSA9IGRmX2RzKSArIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHggPSBsYW1iZGEsIHkgPSBkcykpICsgIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTQpICsgbGFicyh4ID0gZXhwcmVzc2lvbihsYW1iZGEpLCB0aXRsZSA9ICJSTVNFIiwgeSA9ICIiKSApCmdnc2F2ZSgiLi4vZmlndXJlcy9sYW1iZGFfcHVyZV9STVNFLnBkZiIsIGxhbWJkYV9wdXJlX1JNU0UpCgojIG11IC0tLS0tLS0tLS0tLS0tCm11cyA8LSBzZXEoLTMsIDMsIDAuMSkKY2xvdWQgPC0gYXMubWF0cml4KGV4cGFuZC5ncmlkKGMoMyksIG11cyxjKDEpKSkKY29sbmFtZXMoY2xvdWQpIDwtIGxfbSRwYXJtX25hbWVzClggPC0gc2FtcGxlX1gobF9tICwgY2xvdWQsIFIgPSAxMDAwMCkKcHBfZmFrZSA8LSBjb21wdXRlX3B1cmVfcHJlbWlhKFgsIGNvdmVyYWdlX3R5cGUgPSAieHMiLCB0aHNfcHJlbWl1bSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiMgQ2FsY3VsYXRpb24gb2YgdGhlIFJNU0UgYmV0d2VlbiB0cnVlIGFuZCB3cm9uZyBwdXJlIHByZW1pdW0KZHMgPC0gc2FwcGx5KDE6bnJvdyhjbG91ZCksIGZ1bmN0aW9uKGspIG1lYW4oKHBwX2Zha2VbLCBrXSAtIGRhdGEkeCleMileKDEvMikpIApkZl9kcyA8LSBkYXRhLmZyYW1lKGNsb3VkKQpkZl9kcyA8LSBjYmluZChkZl9kcywgZHMpCihtdV9wdXJlX1JNU0UgPC0gZ2dwbG90KGRhdGEgPSBkZl9kcykgKyBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyh4ID0gbXUsIHkgPSBkcykpICsgIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTQpICsgbGFicyh4ID0gZXhwcmVzc2lvbihtdSksIHRpdGxlID0gIlJNU0UiLCB5ID0gIiIpICkKZ2dzYXZlKCIuLi9maWd1cmVzL211X3B1cmVfUk1TRS5wZGYiLCBtdV9wdXJlX1JNU0UpCmBgYApFYWNoIHBhcmFtZXRlcnMgY2FuIGJlIGlkZW50aWZpZWQgc2VwYXJhdGVseSwgbm93IG9mIGNvdXJzZSBpZGVudGlmeWluZyB0aGVtIGpvaW50bHkgaXMgYW5vdGhlciBtYXR0ZXIsIHRoZXJlIG1pZ2h0IGV4aXN0cyBzZXZlcmFsIGNvbWJpbmF0aW9ucyBvZiBwYXJhbWV0ZXJzIHRoYXQgeWllbGQgdGhlIHNhbWUgcHVyZSBwcmVtaXVtLiBPdXIgb3B0aW1pemF0aW9uIGFsbG93cyB1cyB0byBpbnZlc3RpZ2F0ZSBieSBzZWFyY2hpbmcgZWZmaWNpZW50bHkgdGhlIHBhcmFtZXRlciBzcGFjZS4gCgpXZSB1c2UgdGhlIGZvbGx3aW5nIHByaW9yIGFzc3VtcHRpb25zOgokJApcbGFtYmRhXHNpbVx0ZXh0e1VuaWZ9KDAsIDEwKVx0ZXh0eywgfVxtdVxzaW1cdGV4dHtVbmlmfSgtMywgMylcdGV4dHssIGFuZCB9XHNpZ21hXHNpbVx0ZXh0e1VuaWZ9KDAsIDUpCiQkCmFuZCB0aGUgZm9sbG93aW5nIHNldHRpbmdzIGZvciB0aGUgYWxnb3JpdGhtCgokJApKID0gNTAwXHRleHR7IGFuZCB9UiA9IDUwMAokJApSZWNhbGwgdGhhdCAkSiQgaXMgdGhlIHBvcHVsYXRpb24gc2l6ZSBvZiB0aGUgY2xvdWRzIG9mIHBhcnRpY2xlcyBhbmQgJFIkIGlzIHRoZSBudW1iZXIgb2YgTW9udGUgQ2FybG8gcmVwbGljYXRpb25zLiAKCmBgYHtyfQojIFByaW9yIHNldHRpbmdzIGZvciB0aGUgcGFyYW1ldGVycwpwcmlvcl9sYW1iZGEgPC0gcHJpb3JfZGlzdCgidW5pZm9ybSIsICJsYW1iZGEiLGMoMCwgMTApKQpwcmlvcl9tdSA8LSBwcmlvcl9kaXN0KCJ1bmlmb3JtIiwgIm11IixjKC0zLDMpKQpwcmlvcl9zaWdtYSA8LSBwcmlvcl9kaXN0KCJ1bmlmb3JtIiwgInNpZ21hIiwgYygwLCA1KSkKcHJpb3JfZGlzdHJpYnV0aW9ucyA8LSBsaXN0KHByaW9yX2xhbWJkYSwgcHJpb3JfbXUsIHByaW9yX3NpZ21hKQptb2RlbF9wcmlvciA8LSBpbmRlcGVuZGVudF9wcmlvcnMocHJpb3JfZGlzdHJpYnV0aW9ucykKCiMgU2V0dGluZ3MgZm9yIHRoZSBhYmMgYWxnb3JpdGhtCnBvcFNpemUgPC0gNTAwOyAgTUNfUjwtIDUwMDsgYWNjIDwtIDAuMDE7IHNwX2JvdW5kcyA8LSBjKDEsIDEpOyBlcHNfbWluIDwtIDAuMDIKCnN1bShzZHMvc3FydChNQ19SKV4yKQoKZGF0YSA8LSBkZl9wcFssYygiciIsICJkIiwgImwiKV0KZGF0YVsieCJdIDwtIGRmX3BwJHBwCiMgcmVzX2FiYyA8LSBhYmMoZGF0YSwgbW9kZWxfcHJpb3IsIGxfbSwgcF9wLCBwb3BTaXplLCBNQ19SLCBhY2MsIHNwX2JvdW5kcywgZXBzX21pbikKIyBzYXZlKHJlc19hYmMsIGZpbGUgPSAiLi4vZGF0YS9yZXNfYWJjX3N1cHBfbWF0ZXJpYWxfU2VjdGlvbjEuUkRhdGEiKQpgYGAKCmBgYHtyfQpsb2FkKCIuLi9kYXRhL3Jlc19hYmNfc3VwcF9tYXRlcmlhbF9TZWN0aW9uMS5SRGF0YSIpCiMgV2UgcmV0cmlldmUgdGhlIGNsb3VkIG9mIHBhcnRpY2xlcyBhc3NvY2lhdGVkIHRvIGVhY2ggZ2VuZXJhdGlvbgpjbG91ZF9kZnMgPC0gbGFwcGx5KHJlc19hYmMkQ2xvdWRzLCBhcy5kYXRhLmZyYW1lKQpjbG91ZF9kZiA8LSBkby5jYWxsKHJiaW5kLCBjbG91ZF9kZnMpCmNsb3VkX2RmJGdlbmVyYXRpb24gPC0gc29ydChyZXAoMTpsZW5ndGgocmVzX2FiYyRDbG91ZHMpLCBwb3BTaXplKSkKY2xvdWRfZGZfcGxvdCA8LSByYmluZCgKICBkYXRhLmZyYW1lKHBhcm0gPSBjbG91ZF9kZiRsYW1iZGEsIHBhcm1fbmFtZSA9ICJsYW1iZGEiLCBnZW5lcmF0aW9uID0gY2xvdWRfZGYkZ2VuZXJhdGlvbiksCiAgZGF0YS5mcmFtZShwYXJtID0gY2xvdWRfZGYkc2lnbWEsIHBhcm1fbmFtZSA9ICJzaWdtYSIsIGdlbmVyYXRpb24gPSBjbG91ZF9kZiRnZW5lcmF0aW9uKQopCmNsb3VkX2RmX3Bsb3QkcGFybV9uYW1lIDwtYXMuZmFjdG9yKGNsb3VkX2RmX3Bsb3QkcGFybV9uYW1lKSAKbGV2ZWxzKGNsb3VkX2RmX3Bsb3QkcGFybV9uYW1lKSA8LSBjKGxhbWJkYSA9IFRlWCgiJFxcbGFtYmRhJCIpLCBzaWdtYSA9IFRlWCgiJFxcc2lnbWEkIikpCgoocG9zdGVyaW9yX2xhbWJkYSA8LSBnZ3Bsb3QyOjpnZ3Bsb3QoZGF0YSA9IGNsb3VkX2RmICkgKyBnZ3Bsb3QyOjpnZW9tX2RlbnNpdHkoZGF0YSA9IGNsb3VkX2RmLCBtYXBwaW5nID0gYWVzKHg9bGFtYmRhLCBjb2xvciA9IGFzLmZhY3RvcihnZW5lcmF0aW9uKSkpICsgZ2dwbG90Mjo6bGFicyh4PVRlWCgiJFxcbGFtYmRhJCIpLCB5ID0gIkRlbnNpdHkiLCBjb2xvciA9ICJHZW5lcmF0aW9uIikgKyBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDIwKSArIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArIGdncGxvdDI6Omdlb21fdmxpbmUoZ2dwbG90Mjo6YWVzKHhpbnRlcmNlcHQ9dHJ1ZV9wYXJtc1ssMV0pLCBjb2xvcj0iYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiKSkKZ2dzYXZlKCIuLi9maWd1cmVzL3Bvc3Rlcmlvcl9sYW1iZGFfc3VwcF9tYXQucGRmIiwgcG9zdGVyaW9yX2xhbWJkYSkKCihwb3N0ZXJpb3JfbXUgPC0gZ2dwbG90Mjo6Z2dwbG90KGRhdGEgPSBjbG91ZF9kZiApICsgZ2dwbG90Mjo6Z2VvbV9kZW5zaXR5KGRhdGEgPSBjbG91ZF9kZiwgbWFwcGluZyA9IGFlcyh4PW11LCBjb2xvciA9IGFzLmZhY3RvcihnZW5lcmF0aW9uKSkpICsgZ2dwbG90Mjo6bGFicyh4PVRlWCgiJFxcbXUkIiksIHkgPSAiRGVuc2l0eSIsIGNvbG9yID0gIkdlbmVyYXRpb24iKSArIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMjApICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsgZ2dwbG90Mjo6Z2VvbV92bGluZShnZ3Bsb3QyOjphZXMoeGludGVyY2VwdD10cnVlX3Bhcm1zWywyXSksIGNvbG9yPSJibGFjayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIpKQpnZ3NhdmUoIi4uL2ZpZ3VyZXMvcG9zdGVyaW9yX211X3N1cHBfbWF0LnBkZiIsIHBvc3Rlcmlvcl9tdSkKCihwb3N0ZXJpb3Jfc2lnIDwtIGdncGxvdDI6OmdncGxvdChkYXRhID0gY2xvdWRfZGYgKSArIGdncGxvdDI6Omdlb21fZGVuc2l0eShkYXRhID0gY2xvdWRfZGYsIG1hcHBpbmcgPSBhZXMoeD1zaWdtYSwgY29sb3IgPSBhcy5mYWN0b3IoZ2VuZXJhdGlvbikpKSArIGdncGxvdDI6OmxhYnMoeD1UZVgoIiRcXHNpZ21hJCIpLCB5ID0gIkRlbnNpdHkiLCBjb2xvciA9ICJHZW5lcmF0aW9uIikgKyBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDIwKSArIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArIGdncGxvdDI6Omdlb21fdmxpbmUoZ2dwbG90Mjo6YWVzKHhpbnRlcmNlcHQ9dHJ1ZV9wYXJtc1ssM10pLCBjb2xvcj0iYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiKSkKZ2dzYXZlKCIuLi9maWd1cmVzL3Bvc3Rlcmlvcl9zaWdfc3VwcF9tYXQucGRmIiwgcG9zdGVyaW9yX3NpZykKCmBgYAoKIyBJSSAtIE9wdGltaXphdGlvbiBiYXNlZCBvbiB0aGUgY29tbWVyY2lhbCBwcmVtaXVtcyB3aXRob3V0IHJlZ3VsYXJpemF0aW9uCgpXZSBkbyBub3QgaGF2ZSBhY2Nlc3MgdG8gdGhlIHB1cmUgcHJlbWl1bSwgaW5zdGVhZCB3ZSBoYXZlIHRoZSBjb21tZXJjaWFsIHByZW1pdW1zCgokJApcdGlsZGV7cH1faSA9IGZfaVxsZWZ0W1xtYXRoYmJ7RX0oZ19pKFgpKVxyaWdodF0sXHRleHR7IH1pID0gMSxcbGRvdHMsIG4sCiQkCgp3aGVyZSB0aGUgbG9hZGluZyBmdW5jdGlvbnMgbXVzdCBiZSBhcHByb3hpbWF0ZWQuIFdlIHRoZXJlZm9yZSBoYXZlIHRvIGNob29zZSBhIG1vZGVsICRmJCBzdWNoIHRoYXQgCgokJApcdGlsZGV7cH1faSA9IGYocF97aX1eXHRoZXRhKSxcdGV4dHsgfWkgPSAxLFxsZG90cywgbiAgCiQkCmFuZCBob3BlIHRoYXQgdGhlcmUgZXhpc3RzIGEgdW5pcXVlICRcdGhldGFeKiQgc3VjaCB0aGF0IAoKJCQKXHRoZXRhXiogPSBcdW5kZXJzZXR7XHRoZXRhXGluIFxUaGV0YX17XHRleHR7YXJnbWlufX1cLCBkXGxlZnRbXHRpbGRle3B9X3sxOm59LGYocF97MTpufV5cdGhldGEpXHJpZ2h0XS4KJCQKVGhpcyB0eXBpY2FsbHkgZGVwZW5kcyBvbiB0aGUgY2hvaWNlIGZvciAkZiQgd2UgY29uc2lkZXIgaXNvdG9uaWMgYW5kIGxpbmVhci4gSXNvdG9uaWMgcmVncmVzc2lvbiBpbXBvc2UgbW9ub3RvbmljaXR5IHdoaWNoIGlzIGRlc2lyYWJsZSBzaW5jZSB3ZSBiZWxpZXZlIGl0IHdvdWxkIG1ha2Ugc2Vuc2UgaWYgCgokJApwX2kgPiBwX2pcUmlnaHRhcnJvdyBcdGlsZGV7cH1faSA+IFx0aWxkZXtwfV9qLgokJAoKRnVydGhlcm1vcmUgaXNvdG9uaWMgcmVncmVzc2lvbiBpcyBub24gcGFyYW1ldHJpYyB3aGljaCBtaXRpZ2F0ZSB0aGUgcmlzayBvZiBtaXNzcGVjaWZpY2F0aW9uLiBUaGUgZHJhd2JhY2sgaXMgb3ZlcmZpdHRpbmcgYXMgcGVyZmVjdCBpbnRlcnBvbGF0aW9uIG9mIG1heSBvY2N1ciBpbiBhIHNtYWxsIHNhbXBsZSBzaXplIHNldHRpbmcuIAoKIyMgV2VsbC1zcGVjaWZpZWQgY2FzZQoKSW4gb3VyIGV4YW1wbGUgd2UgdGFrZSB0aGUgZm9sbG93aW5nIGxvYWRpbmcgZnV1bmN0aW9ucyAKCiQkCmZfMSh4KSA9IDEuMzhcY2RvdCB4XHRleHR7LCB9Zl8yKHgpID0gMS4xXGNkb3QgeCBcdGV4dHssIGFuZCB9IGZfMyh4KSA9IDEuNFxjZG90IHgKJCQKCmBgYHtyfQpkYXRhIDwtIGRmX3BwWyxjKCJyIiwgImQiLCAibCIpXQpkYXRhWyJ4Il0gPC0gYyhkZl9wcCRwcFsxXSoxLjM4ICwgZGZfcHAkcHBbMl0qMS4xICwgZGZfcHAkcHBbM10gKiAxLjQgKQpkYXRhJHg7ZGZfcHAkcHAKIyBkYXRhWyJ4Il0gPC0gZGZfcHAkcHAKdGhzX3ByZW1pdW0gPC0gYXMubWF0cml4KGRhdGFbcF9wJHBhcm1fbmFtZXNdKQpsYW1iZGFfdHJ1ZSA8LSAzOyBtdV90cnVlIDwtIDA7IHNpZ21hX3RydWUgPC0gMQpyZXNfaXNvIDwtIGlzb3JlZyhkZl9wcCRwcCwgZGF0YSR4KQoKKHBwX2NwX3dlbGxfc3BlY2lmaWVkIDwtIGdncGxvdCgpICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBkZl9wcCRwcCwgeSA9IGRhdGEkeCkpICsgZ2VvbV9zdGVwKG1hcHBpbmcgPSBhZXMoeD0gZGZfcHAkcHAsIHkgPSBkYXRhJHgpKSArIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTQpICsgbGFicyh4ID0gIlB1cmUgUHJlbWl1bSIsIHRpdGxlID0gIkNvbW1lcmNpYWwgcHJlbWl1bSIsIHkgPSAiIikgKQpnZ3NhdmUoIi4uL2ZpZ3VyZXMvcHBfY3Bfd2VsbF9zcGVjaWZpZWQucGRmIiwgcHBfY3Bfd2VsbF9zcGVjaWZpZWQpCmBgYAoKYGBge3J9CiMgU2lnbWEgLS0tLS0tLS0tLS0tLS0KIyBQdXJlIHByZW1pdW0gY2FsY3VsYXRpb24gZm9yIGEgcmFuZ2Ugb2YgcmlzayBtb2RlbCBwYXJhbWV0ZXJzCnNpZ21hcyA8LSBzZXEoMC4xLCAyLCAwLjAyKQpjbG91ZCA8LSBhcy5tYXRyaXgoZXhwYW5kLmdyaWQoYygzKSwgYygwKSxzaWdtYXMpKQpjb2xuYW1lcyhjbG91ZCkgPC0gbF9tJHBhcm1fbmFtZXMKWCA8LSBzYW1wbGVfWChsX20gLCBjbG91ZCwgUiA9IDEwMDAwKQpwcF9mYWtlIDwtIGNvbXB1dGVfcHVyZV9wcmVtaWEoWCwgY292ZXJhZ2VfdHlwZSA9ICJ4cyIsIHRoc19wcmVtaXVtKQojIENhbGN1bGF0aW9uIG9mIHRoZSBSTVNFIGJldHdlZW4gdHJ1ZSBhbmQgd3JvbmcgY29tbWVyY2lhbCBwcmVtaXVtCnJlc3NfaXNvIDwtIGxhcHBseSgxOm5jb2wocHBfZmFrZSksIGZ1bmN0aW9uKGspIGlzb3JlZyhwcF9mYWtlWywga10sIGRhdGEkeCkpCgpyZXNzX2xtIDwtIHNhcHBseSgxOm5jb2wocHBfZmFrZSksIGZ1bmN0aW9uKGspIGFzLm51bWVyaWMobG0oZGF0YSR4fnBwX2Zha2VbLCBrXSAtIDEpJGNvZWZmaWNpZW50cykpCnJlc3NfbG1bc2lnbWFzID09IDFdCmFzLm51bWVyaWMobG0oZGF0YSR4IH4gcHBfZmFrZVssIHNpZ21hcyA9PSAxXSAtIDEpJGNvZWZmaWNpZW50cykKbWVhbigobWVhbigocHBfZmFrZVssIHNpZ21hcyA9PSAxXSAqIHJlc3NfbG1bc2lnbWFzID09IDFdIC0gZGF0YSR4KV4yKV4oMS8yKSAtIGRhdGEkeCleMileKDEvMikKZnNfaXNvIDwtIGxhcHBseShyZXNzX2lzbywgZnVuY3Rpb24ocmVzX2lzbykgc3RlcGZ1bihzb3J0KHJlc19pc28keCksIGMobWluKHJlc19pc28keWYpIC8yICwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc19pc28keWYpLCBmID0gMSkpCmRzX2lzbyA8LSBzYXBwbHkoMTpucm93KGNsb3VkKSwgZnVuY3Rpb24oaykgbWVhbigoZnNfaXNvW1trXV0ocHBfZmFrZVssIGtdKSAtIGRhdGEkeCleMileKDEvMikpIApkc19sbSA8LSBzYXBwbHkoMTpucm93KGNsb3VkKSwgZnVuY3Rpb24oaykgbWVhbigocHBfZmFrZVssIGtdIC8gcmVzc19sbVtrXSAtIGRhdGEkeCleMileKDEvMikpIAoKZGZfZHMgPC0gZGF0YS5mcmFtZShjbG91ZCkKZGZfZHMgPC0gY2JpbmQoZGZfZHMsIGRzX2lzbykKZGZfZHMgPC0gY2JpbmQoZGZfZHMsIGRzX2xtKQoocm1zZV9zaWdtYV9pc28gPC0gZ2dwbG90KGRhdGEgPSBkZl9kcykgKyBnZW9tX2xpbmUobWFwcGluZyA9IGFlcyh4ID0gc2lnbWEsIHkgPSBkc19pc28pKSArIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTQpICsgbGFicyh4ID0gZXhwcmVzc2lvbihzaWdtYSksIHRpdGxlID0gIlJNU0UiLCB5ID0gIiIpICkKZ2dzYXZlKCIuLi9maWd1cmVzL3Jtc2Vfc2lnbWFfaXNvLnBkZiIsIHJtc2Vfc2lnbWFfaXNvKQoKKHJtc2Vfc2lnbWFfbGluZWFyIDwtIGdncGxvdChkYXRhID0gZGZfZHMpICsgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoeCA9IHNpZ21hLCB5ID0gZHNfbG0pKSArIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTQpICsgbGFicyh4ID0gZXhwcmVzc2lvbihzaWdtYSksIHRpdGxlID0gIlJNU0UiLCB5ID0gIiIpKQpnZ3NhdmUoIi4uL2ZpZ3VyZXMvcm1zZV9zaWdtYV9saW5lYXIucGRmIiwgcm1zZV9zaWdtYV9saW5lYXIpCgpgYGAKYGBge3J9CiMgUHJpb3Igc2V0dGluZ3MgZm9yIHRoZSBwYXJhbWV0ZXJzCnByaW9yX2xhbWJkYSA8LSBwcmlvcl9kaXN0KCJjb25zdGFudCIsICJsYW1iZGEiLGMoMykpCnByaW9yX211IDwtIHByaW9yX2Rpc3QoImNvbnN0YW50IiwgIm11IixjKDApKQpwcmlvcl9zaWdtYSA8LSBwcmlvcl9kaXN0KCJ1bmlmb3JtIiwgInNpZ21hIiwgYygwLCAyKSkKcHJpb3JfZGlzdHJpYnV0aW9ucyA8LSBsaXN0KHByaW9yX2xhbWJkYSwgcHJpb3JfbXUsIHByaW9yX3NpZ21hKQptb2RlbF9wcmlvciA8LSBpbmRlcGVuZGVudF9wcmlvcnMocHJpb3JfZGlzdHJpYnV0aW9ucykKCiMgU2V0dGluZ3MgZm9yIHRoZSBhYmMgYWxnb3JpdGhtCnBvcFNpemUgPC0gMjAwMDsgIE1DX1I8LSA1MDA7IGFjYyA8LSAwLjAxOyBzcF9ib3VuZHMgPC0gYygxMF4oLTE2KSwgSW5mKTsgZXBzX21pbiA8LSAxMF4tOApyZXNfYWJjIDwtIGFiYyhkYXRhLCBtb2RlbF9wcmlvciwgbF9tLCBwX3AsIHBvcFNpemUsIE1DX1IsIGFjYywgc3BfYm91bmRzLCBlcHNfbWluKQpzYXZlKHJlc19hYmMsIGZpbGUgPSAiLi4vZGF0YS9yZXNfYWJjX3NpZ193b19yZWdfc3VwcF9tYXRlcmlhbF9TZWN0aW9uMi5SRGF0YSIpCmBgYApgYGB7cn0KbG9hZCgiLi4vZGF0YS9yZXNfYWJjX3NpZ193b19yZWdfc3VwcF9tYXRlcmlhbF9TZWN0aW9uMi5SRGF0YSIpCmNsb3VkX2RmcyA8LSBsYXBwbHkocmVzX2FiYyRDbG91ZHMsIGFzLmRhdGEuZnJhbWUpCmNsb3VkX2RmIDwtIGRvLmNhbGwocmJpbmQsIGNsb3VkX2RmcykKY2xvdWRfZGYkZ2VuZXJhdGlvbiA8LSBzb3J0KHJlcCgxOmxlbmd0aChyZXNfYWJjJENsb3VkcyksIHBvcFNpemUpKQpjbG91ZF9kZl9wbG90IDwtIGRhdGEuZnJhbWUocGFybSA9IGNsb3VkX2RmJHNpZ21hLCBwYXJtX25hbWUgPSAic2lnbWEiLCBnZW5lcmF0aW9uID0gY2xvdWRfZGYkZ2VuZXJhdGlvbikKCmNsb3VkX2RmX3Bsb3QkcGFybV9uYW1lIDwtYXMuZmFjdG9yKGNsb3VkX2RmX3Bsb3QkcGFybV9uYW1lKSAKbGV2ZWxzKGNsb3VkX2RmX3Bsb3QkcGFybV9uYW1lKSA8LSBjKHNpZ21hID0gVGVYKCIkXFxzaWdtYSQiKSkKKHBvc3Rlcmlvcl9zaWdfaXNvIDwtIGdncGxvdDI6OmdncGxvdChkYXRhID0gY2xvdWRfZGYgKSArIGdncGxvdDI6Omdlb21fZGVuc2l0eShkYXRhID0gY2xvdWRfZGYsIG1hcHBpbmcgPSBhZXMoeD1zaWdtYSwgY29sb3IgPSBhcy5mYWN0b3IoZ2VuZXJhdGlvbikpKSArIGdncGxvdDI6OmxhYnMoeD1UZVgoIiRcXHNpZ21hJCIpLCB5ID0gIkRlbnNpdHkiLCBjb2xvciA9ICJHZW5lcmF0aW9uIikgKyBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDIwKSArIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArIGdncGxvdDI6Omdlb21fdmxpbmUoZ2dwbG90Mjo6YWVzKHhpbnRlcmNlcHQ9dHJ1ZV9wYXJtc1ssM10pLCBjb2xvcj0iYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiKSkKZ2dzYXZlKCIuLi9maWd1cmVzL3Bvc3Rlcmlvcl9zaWdfaXNvX3N1cHBfbWF0LnBkZiIsIHBvc3Rlcmlvcl9zaWdfaXNvKQpgYGAKCmBgYHtyfQojIFByaW9yIHNldHRpbmdzIGZvciB0aGUgcGFyYW1ldGVycwpwcmlvcl9sYW1iZGEgPC0gcHJpb3JfZGlzdCgidW5pZm9ybSIsICJsYW1iZGEiLGMoMCwgNSkpCnByaW9yX211IDwtIHByaW9yX2Rpc3QoInVuaWZvcm0iLCAibXUiLGMoLTMsMykpCnByaW9yX3NpZ21hIDwtIHByaW9yX2Rpc3QoInVuaWZvcm0iLCAic2lnbWEiLCBjKDAsIDIpKQpwcmlvcl9kaXN0cmlidXRpb25zIDwtIGxpc3QocHJpb3JfbGFtYmRhLCBwcmlvcl9tdSwgcHJpb3Jfc2lnbWEpCm1vZGVsX3ByaW9yIDwtIGluZGVwZW5kZW50X3ByaW9ycyhwcmlvcl9kaXN0cmlidXRpb25zKQoKIyBTZXR0aW5ncyBmb3IgdGhlIGFiYyBhbGdvcml0aG0KcG9wU2l6ZSA8LSA1MDA7ICBNQ19SPC0gNTAwOyBhY2MgPC0gMC4wMTsgc3BfYm91bmRzIDwtIGMoMTBeKC0xNiksIEluZik7IGVwc19taW4gPC0gMTBeKC04KQojIHJlc19hYmMgPC0gYWJjKGRhdGEsIG1vZGVsX3ByaW9yLCBsX20sIHBfcCwgcG9wU2l6ZSwgTUNfUiwgYWNjLCBzcF9ib3VuZHMsIGVwc19taW4pCiMgc2F2ZShyZXNfYWJjLCBmaWxlID0gIi4uL2RhdGEvcmVzX2FiY193b19yZWdfc3VwcF9tYXRlcmlhbF9TZWN0aW9uMi5SRGF0YSIpCmBgYAoKYGBge3J9CmxvYWQoIi4uL2RhdGEvcmVzX2FiY193b19yZWdfc3VwcF9tYXRlcmlhbF9TZWN0aW9uMi5SRGF0YSIpCiMgV2UgcmV0cmlldmUgdGhlIGNsb3VkIG9mIHBhcnRpY2xlcyBhc3NvY2lhdGVkIHRvIGVhY2ggZ2VuZXJhdGlvbgpjbG91ZF9kZnMgPC0gbGFwcGx5KHJlc19hYmMkQ2xvdWRzLCBhcy5kYXRhLmZyYW1lKQpjbG91ZF9kZiA8LSBkby5jYWxsKHJiaW5kLCBjbG91ZF9kZnMpCmNsb3VkX2RmJGdlbmVyYXRpb24gPC0gc29ydChyZXAoMTpsZW5ndGgocmVzX2FiYyRDbG91ZHMpLCBwb3BTaXplKSkKY2xvdWRfZGZfcGxvdCA8LSByYmluZCgKICBkYXRhLmZyYW1lKHBhcm0gPSBjbG91ZF9kZiRsYW1iZGEsIHBhcm1fbmFtZSA9ICJsYW1iZGEiLCBnZW5lcmF0aW9uID0gY2xvdWRfZGYkZ2VuZXJhdGlvbiksCiAgICBkYXRhLmZyYW1lKHBhcm0gPSBjbG91ZF9kZiRzaWdtYSwgcGFybV9uYW1lID0gIm11IiwgZ2VuZXJhdGlvbiA9IGNsb3VkX2RmJGdlbmVyYXRpb24pLAogIGRhdGEuZnJhbWUocGFybSA9IGNsb3VkX2RmJHNpZ21hLCBwYXJtX25hbWUgPSAic2lnbWEiLCBnZW5lcmF0aW9uID0gY2xvdWRfZGYkZ2VuZXJhdGlvbikKKQpjbG91ZF9kZl9wbG90JHBhcm1fbmFtZSA8LWFzLmZhY3RvcihjbG91ZF9kZl9wbG90JHBhcm1fbmFtZSkgCmxldmVscyhjbG91ZF9kZl9wbG90JHBhcm1fbmFtZSkgPC0gYyhsYW1iZGEgPSBUZVgoIiRcXGxhbWJkYSQiKSwgbXUgPSBUZVgoIiRcXG11JCIpLCAgc2lnbWEgPSBUZVgoIiRcXHNpZ21hJCIpKQoKKHBvc3Rlcmlvcl9sYW1iZGEgPC0gZ2dwbG90Mjo6Z2dwbG90KGRhdGEgPSBjbG91ZF9kZiApICsgZ2dwbG90Mjo6Z2VvbV9kZW5zaXR5KGRhdGEgPSBjbG91ZF9kZiwgbWFwcGluZyA9IGFlcyh4PWxhbWJkYSwgY29sb3IgPSBhcy5mYWN0b3IoZ2VuZXJhdGlvbikpKSArIGdncGxvdDI6OmxhYnMoeD1UZVgoIiRcXGxhbWJkYSQiKSwgeSA9ICJEZW5zaXR5IiwgY29sb3IgPSAiR2VuZXJhdGlvbiIpICsgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAyMCkgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKyBnZ3Bsb3QyOjpnZW9tX3ZsaW5lKGdncGxvdDI6OmFlcyh4aW50ZXJjZXB0PXRydWVfcGFybXNbLDFdKSwgY29sb3I9ImJsYWNrIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIikpCmdnc2F2ZSgiLi4vZmlndXJlcy9wb3N0ZXJpb3JfbGFtYmRhX3dvX3JlZ19zdXBwX21hdC5wZGYiLCBwb3N0ZXJpb3JfbGFtYmRhKQoKKHBvc3Rlcmlvcl9tdSA8LSBnZ3Bsb3QyOjpnZ3Bsb3QoZGF0YSA9IGNsb3VkX2RmICkgKyBnZ3Bsb3QyOjpnZW9tX2RlbnNpdHkoZGF0YSA9IGNsb3VkX2RmLCBtYXBwaW5nID0gYWVzKHg9bXUsIGNvbG9yID0gYXMuZmFjdG9yKGdlbmVyYXRpb24pKSkgKyBnZ3Bsb3QyOjpsYWJzKHg9VGVYKCIkXFxtdSQiKSwgeSA9ICJEZW5zaXR5IiwgY29sb3IgPSAiR2VuZXJhdGlvbiIpICsgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAyMCkgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKyBnZ3Bsb3QyOjpnZW9tX3ZsaW5lKGdncGxvdDI6OmFlcyh4aW50ZXJjZXB0PXRydWVfcGFybXNbLDJdKSwgY29sb3I9ImJsYWNrIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIikpCmdnc2F2ZSgiLi4vZmlndXJlcy9wb3N0ZXJpb3JfbXVfd29fcmVnX3N1cHBfbWF0LnBkZiIsIHBvc3Rlcmlvcl9tdSkKCihwb3N0ZXJpb3Jfc2lnIDwtIGdncGxvdDI6OmdncGxvdChkYXRhID0gY2xvdWRfZGYgKSArIGdncGxvdDI6Omdlb21fZGVuc2l0eShkYXRhID0gY2xvdWRfZGYsIG1hcHBpbmcgPSBhZXMoeD1zaWdtYSwgY29sb3IgPSBhcy5mYWN0b3IoZ2VuZXJhdGlvbikpKSArIGdncGxvdDI6OmxhYnMoeD1UZVgoIiRcXHNpZ21hJCIpLCB5ID0gIkRlbnNpdHkiLCBjb2xvciA9ICJHZW5lcmF0aW9uIikgKyBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDIwKSArIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArIGdncGxvdDI6Omdlb21fdmxpbmUoZ2dwbG90Mjo6YWVzKHhpbnRlcmNlcHQ9dHJ1ZV9wYXJtc1ssM10pLCBjb2xvcj0iYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiKSkKZ2dzYXZlKCIuLi9maWd1cmVzL3Bvc3Rlcmlvcl9zaWdfd29fcmVnX3N1cHBfbWF0LnBkZiIsIHBvc3Rlcmlvcl9zaWcpCmBgYAoKYGBge3J9CiMgUHJpb3Igc2V0dGluZ3MgZm9yIHRoZSBwYXJhbWV0ZXJzCnByaW9yX2xhbWJkYSA8LSBwcmlvcl9kaXN0KCJ1bmlmb3JtIiwgImxhbWJkYSIsYygwLCA1KSkKcHJpb3JfbXUgPC0gcHJpb3JfZGlzdCgidW5pZm9ybSIsICJtdSIsYygtMywzKSkKcHJpb3Jfc2lnbWEgPC0gcHJpb3JfZGlzdCgidW5pZm9ybSIsICJzaWdtYSIsIGMoMCwgMikpCnByaW9yX2Rpc3RyaWJ1dGlvbnMgPC0gbGlzdChwcmlvcl9sYW1iZGEsIHByaW9yX211LCBwcmlvcl9zaWdtYSkKbW9kZWxfcHJpb3IgPC0gaW5kZXBlbmRlbnRfcHJpb3JzKHByaW9yX2Rpc3RyaWJ1dGlvbnMpCgojIFNldHRpbmdzIGZvciB0aGUgYWJjIGFsZ29yaXRobQpwb3BTaXplIDwtIDUwMDsgIE1DX1I8LSA1MDA7IGFjYyA8LSAwLjAwMTsgc3BfYm91bmRzIDwtIGMobWluKGRmX3BwJHBwIC8gZGF0YSR4KSwgbWF4KGRmX3BwJHBwIC8gZGF0YSR4KSkKZXBzX21pbiA8LSAwLjAwMDEKIyByZXNfYWJjIDwtIGFiYyhkYXRhLCBtb2RlbF9wcmlvciwgbF9tLCBwX3AsIHBvcFNpemUsIE1DX1IsIGFjYywgc3BfYm91bmRzLCBlcHNfbWluKQojIHNhdmUocmVzX2FiYywgZmlsZSA9ICIuLi9kYXRhL3Jlc19hYmNfd19yZWdfc3VwcF9tYXRlcmlhbF9TZWN0aW9uMi5SRGF0YSIpCmBgYAoKYGBge3J9CmxvYWQoIi4uL2RhdGEvcmVzX2FiY193X3JlZ19zdXBwX21hdGVyaWFsX1NlY3Rpb24yLlJEYXRhIikKCiMgV2UgcmV0cmlldmUgdGhlIGNsb3VkIG9mIHBhcnRpY2xlcyBhc3NvY2lhdGVkIHRvIGVhY2ggZ2VuZXJhdGlvbgpjbG91ZF9kZnMgPC0gbGFwcGx5KHJlc19hYmMkQ2xvdWRzLCBhcy5kYXRhLmZyYW1lKQpjbG91ZF9kZiA8LSBkby5jYWxsKHJiaW5kLCBjbG91ZF9kZnMpCmNsb3VkX2RmJGdlbmVyYXRpb24gPC0gc29ydChyZXAoMTpsZW5ndGgocmVzX2FiYyRDbG91ZHMpLCBwb3BTaXplKSkKY2xvdWRfZGZfcGxvdCA8LSByYmluZCgKICBkYXRhLmZyYW1lKHBhcm0gPSBjbG91ZF9kZiRsYW1iZGEsIHBhcm1fbmFtZSA9ICJsYW1iZGEiLCBnZW5lcmF0aW9uID0gY2xvdWRfZGYkZ2VuZXJhdGlvbiksCiAgICBkYXRhLmZyYW1lKHBhcm0gPSBjbG91ZF9kZiRzaWdtYSwgcGFybV9uYW1lID0gIm11IiwgZ2VuZXJhdGlvbiA9IGNsb3VkX2RmJGdlbmVyYXRpb24pLAogIGRhdGEuZnJhbWUocGFybSA9IGNsb3VkX2RmJHNpZ21hLCBwYXJtX25hbWUgPSAic2lnbWEiLCBnZW5lcmF0aW9uID0gY2xvdWRfZGYkZ2VuZXJhdGlvbikKKQpjbG91ZF9kZl9wbG90JHBhcm1fbmFtZSA8LWFzLmZhY3RvcihjbG91ZF9kZl9wbG90JHBhcm1fbmFtZSkgCmxldmVscyhjbG91ZF9kZl9wbG90JHBhcm1fbmFtZSkgPC0gYyhsYW1iZGEgPSBUZVgoIiRcXGxhbWJkYSQiKSwgbXUgPSBUZVgoIiRcXG11JCIpLCAgc2lnbWEgPSBUZVgoIiRcXHNpZ21hJCIpKQoKKHBvc3Rlcmlvcl9sYW1iZGEgPC0gZ2dwbG90Mjo6Z2dwbG90KGRhdGEgPSBjbG91ZF9kZiApICsgZ2dwbG90Mjo6Z2VvbV9kZW5zaXR5KGRhdGEgPSBjbG91ZF9kZiwgbWFwcGluZyA9IGFlcyh4PWxhbWJkYSwgY29sb3IgPSBhcy5mYWN0b3IoZ2VuZXJhdGlvbikpKSArIGdncGxvdDI6OmxhYnMoeD1UZVgoIiRcXGxhbWJkYSQiKSwgeSA9ICJEZW5zaXR5IiwgY29sb3IgPSAiR2VuZXJhdGlvbiIpICsgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAyMCkgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKyBnZ3Bsb3QyOjpnZW9tX3ZsaW5lKGdncGxvdDI6OmFlcyh4aW50ZXJjZXB0PXRydWVfcGFybXNbLDFdKSwgY29sb3I9ImJsYWNrIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIikpCmdnc2F2ZSgiLi4vZmlndXJlcy9wb3N0ZXJpb3JfbGFtYmRhX3dfcmVnX3N1cHBfbWF0LnBkZiIsIHBvc3Rlcmlvcl9sYW1iZGEpCgoocG9zdGVyaW9yX211IDwtIGdncGxvdDI6OmdncGxvdChkYXRhID0gY2xvdWRfZGYgKSArIGdncGxvdDI6Omdlb21fZGVuc2l0eShkYXRhID0gY2xvdWRfZGYsIG1hcHBpbmcgPSBhZXMoeD1tdSwgY29sb3IgPSBhcy5mYWN0b3IoZ2VuZXJhdGlvbikpKSArIGdncGxvdDI6OmxhYnMoeD1UZVgoIiRcXG11JCIpLCB5ID0gIkRlbnNpdHkiLCBjb2xvciA9ICJHZW5lcmF0aW9uIikgKyBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDIwKSArIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArIGdncGxvdDI6Omdlb21fdmxpbmUoZ2dwbG90Mjo6YWVzKHhpbnRlcmNlcHQ9dHJ1ZV9wYXJtc1ssMl0pLCBjb2xvcj0iYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiKSkKZ2dzYXZlKCIuLi9maWd1cmVzL3Bvc3Rlcmlvcl9tdV93X3JlZ19zdXBwX21hdC5wZGYiLCBwb3N0ZXJpb3JfbXUpCgoocG9zdGVyaW9yX3NpZyA8LSBnZ3Bsb3QyOjpnZ3Bsb3QoZGF0YSA9IGNsb3VkX2RmICkgKyBnZ3Bsb3QyOjpnZW9tX2RlbnNpdHkoZGF0YSA9IGNsb3VkX2RmLCBtYXBwaW5nID0gYWVzKHg9c2lnbWEsIGNvbG9yID0gYXMuZmFjdG9yKGdlbmVyYXRpb24pKSkgKyBnZ3Bsb3QyOjpsYWJzKHg9VGVYKCIkXFxzaWdtYSQiKSwgeSA9ICJEZW5zaXR5IiwgY29sb3IgPSAiR2VuZXJhdGlvbiIpICsgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAyMCkgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKyBnZ3Bsb3QyOjpnZW9tX3ZsaW5lKGdncGxvdDI6OmFlcyh4aW50ZXJjZXB0PXRydWVfcGFybXNbLDNdKSwgY29sb3I9ImJsYWNrIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIikpCmdnc2F2ZSgiLi4vZmlndXJlcy9wb3N0ZXJpb3Jfc2lnX3dfcmVnX3N1cHBfbWF0LnBkZiIsIHBvc3Rlcmlvcl9zaWcpCgpgYGAKCiMjIE1pc3Mtc3BlY2lmaWVkIGNhc2UKCkluIG91ciBleGFtcGxlIHdlIHRha2UgdGhlIGZvbGxvd2luZyBsb2FkaW5nIGZ1dW5jdGlvbnMgCgokJApmXzEoeCkgPSAxLjFcY2RvdCB4XHRleHR7LCB9Zl8yKHgpID0gMS4yXGNkb3QgeCBcdGV4dHssIGFuZCB9IGZfMyh4KSA9IDEuM1xjZG90IHgKJCQKCmBgYHtyfQpkYXRhIDwtIGRmX3BwWyxjKCJyIiwgImQiLCAibCIpXQpkYXRhWyJ4Il0gPC0gYyhkZl9wcCRwcFsxXSoxLjEgLCBkZl9wcCRwcFsyXSoxLjIgLCBkZl9wcCRwcFszXSAqIDEuMyApCiMgZGF0YVsieCJdIDwtIGRmX3BwJHBwCnRoc19wcmVtaXVtIDwtIGFzLm1hdHJpeChkYXRhW3BfcCRwYXJtX25hbWVzXSkKbGFtYmRhX3RydWUgPC0gMzsgbXVfdHJ1ZSA8LSAwOyBzaWdtYV90cnVlIDwtIDEKcmVzX2lzbyA8LSBpc29yZWcoZGZfcHAkcHAsIGRhdGEkeCkKCihwcF9jcF9taXNzX3NwZWNpZmllZCA8LSBnZ3Bsb3QoKSArIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyh4ID0gZGZfcHAkcHAsIHkgPSBkYXRhJHgpKSArIGdlb21fc3RlcChtYXBwaW5nID0gYWVzKHg9IGRmX3BwJHBwW29yZGVyKGRmX3BwJHBwKV0sIHkgPSByZXNfaXNvJHlmKSkgKyBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE0KSArIGxhYnMoeCA9ICJQdXJlIFByZW1pdW0iLCB0aXRsZSA9ICJDb21tZXJjaWFsIHByZW1pdW0iLCB5ID0gIiIpICkKZ2dzYXZlKCIuLi9maWd1cmVzL3BwX2NwX21pc3Nfc3BlY2lmaWVkLnBkZiIsIHBwX2NwX21pc3Nfc3BlY2lmaWVkKQoKYGBgCgpgYGB7cn0KIyBTaWdtYSAtLS0tLS0tLS0tLS0tLQojIFB1cmUgcHJlbWl1bSBjYWxjdWxhdGlvbiBmb3IgYSByYW5nZSBvZiByaXNrIG1vZGVsIHBhcmFtZXRlcnMKc2lnbWFzIDwtIHNlcSgwLjEsIDIsIDAuMDIpCmNsb3VkIDwtIGFzLm1hdHJpeChleHBhbmQuZ3JpZChjKDMpLCBjKDApLHNpZ21hcykpCmNvbG5hbWVzKGNsb3VkKSA8LSBsX20kcGFybV9uYW1lcwpYIDwtIHNhbXBsZV9YKGxfbSAsIGNsb3VkLCBSID0gMTAwMDApCnBwX2Zha2UgPC0gY29tcHV0ZV9wdXJlX3ByZW1pYShYLCBjb3ZlcmFnZV90eXBlID0gInhzIiwgdGhzX3ByZW1pdW0pCiMgQ2FsY3VsYXRpb24gb2YgdGhlIFJNU0UgYmV0d2VlbiB0cnVlIGFuZCB3cm9uZyBjb21tZXJjaWFsIHByZW1pdW0KcmVzc19pc28gPC0gbGFwcGx5KDE6bmNvbChwcF9mYWtlKSwgZnVuY3Rpb24oaykgaXNvcmVnKHBwX2Zha2VbLCBrXSwgZGF0YSR4KSkKCnJlc3NfbG0gPC0gc2FwcGx5KDE6bmNvbChwcF9mYWtlKSwgZnVuY3Rpb24oaykgYXMubnVtZXJpYyhsbShkYXRhJHh+cHBfZmFrZVssIGtdIC0gMSkkY29lZmZpY2llbnRzKSkKcmVzc19sbVtzaWdtYXMgPT0gMV0KYXMubnVtZXJpYyhsbShkYXRhJHggfiBwcF9mYWtlWywgc2lnbWFzID09IDFdIC0gMSkkY29lZmZpY2llbnRzKQptZWFuKChtZWFuKChwcF9mYWtlWywgc2lnbWFzID09IDFdICogcmVzc19sbVtzaWdtYXMgPT0gMV0gLSBkYXRhJHgpXjIpXigxLzIpIC0gZGF0YSR4KV4yKV4oMS8yKQpmc19pc28gPC0gbGFwcGx5KHJlc3NfaXNvLCBmdW5jdGlvbihyZXNfaXNvKSBzdGVwZnVuKHNvcnQocmVzX2lzbyR4KSwgYyhtaW4ocmVzX2lzbyR5ZikgLzIgLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzX2lzbyR5ZiksIGYgPSAxKSkKZHNfaXNvIDwtIHNhcHBseSgxOm5yb3coY2xvdWQpLCBmdW5jdGlvbihrKSBtZWFuKChmc19pc29bW2tdXShwcF9mYWtlWywga10pIC0gZGF0YSR4KV4yKV4oMS8yKSkgCmRzX2xtIDwtIHNhcHBseSgxOm5yb3coY2xvdWQpLCBmdW5jdGlvbihrKSBtZWFuKChwcF9mYWtlWywga10gLyByZXNzX2xtW2tdIC0gZGF0YSR4KV4yKV4oMS8yKSkgCgpkZl9kcyA8LSBkYXRhLmZyYW1lKGNsb3VkKQpkZl9kcyA8LSBjYmluZChkZl9kcywgZHNfaXNvKQpkZl9kcyA8LSBjYmluZChkZl9kcywgZHNfbG0pCihybXNlX3NpZ21hX2lzbyA8LSBnZ3Bsb3QoZGF0YSA9IGRmX2RzKSArIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHggPSBzaWdtYSwgeSA9IGRzX2lzbykpICsgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNCkgKyBsYWJzKHggPSBleHByZXNzaW9uKHNpZ21hKSwgdGl0bGUgPSAiUk1TRSIsIHkgPSAiIikgKQpnZ3NhdmUoIi4uL2ZpZ3VyZXMvcm1zZV9zaWdtYV9pc29fbWlzcy5wZGYiLCBybXNlX3NpZ21hX2lzbykKCihybXNlX3NpZ21hX2xpbmVhciA8LSBnZ3Bsb3QoZGF0YSA9IGRmX2RzKSArIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHggPSBzaWdtYSwgeSA9IGRzX2xtKSkgKyBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE0KSArIGxhYnMoeCA9IGV4cHJlc3Npb24oc2lnbWEpLCB0aXRsZSA9ICJSTVNFIiwgeSA9ICIiKSkKZ2dzYXZlKCIuLi9maWd1cmVzL3Jtc2Vfc2lnbWFfbGluZWFyX21pc3MucGRmIiwgcm1zZV9zaWdtYV9saW5lYXIpCmBgYAoKCmBgYHtyfQojIFByaW9yIHNldHRpbmdzIGZvciB0aGUgcGFyYW1ldGVycwpwcmlvcl9sYW1iZGEgPC0gcHJpb3JfZGlzdCgidW5pZm9ybSIsICJsYW1iZGEiLGMoMCw1KSkKcHJpb3JfbXUgPC0gcHJpb3JfZGlzdCgidW5pZm9ybSIsICJtdSIsYygtMywzKSkKcHJpb3Jfc2lnbWEgPC0gcHJpb3JfZGlzdCgidW5pZm9ybSIsICJzaWdtYSIsIGMoMCwgMikpCnByaW9yX2Rpc3RyaWJ1dGlvbnMgPC0gbGlzdChwcmlvcl9sYW1iZGEsIHByaW9yX211LCBwcmlvcl9zaWdtYSkKbW9kZWxfcHJpb3IgPC0gaW5kZXBlbmRlbnRfcHJpb3JzKHByaW9yX2Rpc3RyaWJ1dGlvbnMpCgojIFNldHRpbmdzIGZvciB0aGUgYWJjIGFsZ29yaXRobQpwb3BTaXplIDwtIDUwMDsgIE1DX1I8LSA1MDA7IGFjYyA8LSAwLjAxOyBzcF9ib3VuZHMgPC0gYygxMF4oLTE2KSwgSW5mKTsgZXBzX21pbiA8LSAwLjAwMDEKIyByZXNfYWJjIDwtIGFiYyhkYXRhLCBtb2RlbF9wcmlvciwgbF9tLCBwX3AsIHBvcFNpemUsIE1DX1IsIGFjYywgc3BfYm91bmRzLCBlcHNfbWluKQojIHNhdmUocmVzX2FiYywgZmlsZSA9ICIuLi9kYXRhL3Jlc19hYmNfd29fcmVnX21pc3Nfc3VwcF9tYXRlcmlhbF9TZWN0aW9uMi5SRGF0YSIpCgpgYGAKCmBgYHtyfQpsb2FkKCIuLi9kYXRhL3Jlc19hYmNfd29fcmVnX21pc3Nfc3VwcF9tYXRlcmlhbF9TZWN0aW9uMi5SRGF0YSIpCgojIFdlIHJldHJpZXZlIHRoZSBjbG91ZCBvZiBwYXJ0aWNsZXMgYXNzb2NpYXRlZCB0byBlYWNoIGdlbmVyYXRpb24KY2xvdWRfZGZzIDwtIGxhcHBseShyZXNfYWJjJENsb3VkcywgYXMuZGF0YS5mcmFtZSkKY2xvdWRfZGYgPC0gZG8uY2FsbChyYmluZCwgY2xvdWRfZGZzKQpjbG91ZF9kZiRnZW5lcmF0aW9uIDwtIHNvcnQocmVwKDE6bGVuZ3RoKHJlc19hYmMkQ2xvdWRzKSwgcG9wU2l6ZSkpCmNsb3VkX2RmX3Bsb3QgPC0gcmJpbmQoCiAgZGF0YS5mcmFtZShwYXJtID0gY2xvdWRfZGYkbGFtYmRhLCBwYXJtX25hbWUgPSAibGFtYmRhIiwgZ2VuZXJhdGlvbiA9IGNsb3VkX2RmJGdlbmVyYXRpb24pLAogICAgZGF0YS5mcmFtZShwYXJtID0gY2xvdWRfZGYkc2lnbWEsIHBhcm1fbmFtZSA9ICJtdSIsIGdlbmVyYXRpb24gPSBjbG91ZF9kZiRnZW5lcmF0aW9uKSwKICBkYXRhLmZyYW1lKHBhcm0gPSBjbG91ZF9kZiRzaWdtYSwgcGFybV9uYW1lID0gInNpZ21hIiwgZ2VuZXJhdGlvbiA9IGNsb3VkX2RmJGdlbmVyYXRpb24pCikKY2xvdWRfZGZfcGxvdCRwYXJtX25hbWUgPC1hcy5mYWN0b3IoY2xvdWRfZGZfcGxvdCRwYXJtX25hbWUpIApsZXZlbHMoY2xvdWRfZGZfcGxvdCRwYXJtX25hbWUpIDwtIGMobGFtYmRhID0gVGVYKCIkXFxsYW1iZGEkIiksIG11ID0gVGVYKCIkXFxtdSQiKSwgIHNpZ21hID0gVGVYKCIkXFxzaWdtYSQiKSkKCihwb3N0ZXJpb3JfbGFtYmRhIDwtIGdncGxvdDI6OmdncGxvdChkYXRhID0gY2xvdWRfZGYgKSArIGdncGxvdDI6Omdlb21fZGVuc2l0eShkYXRhID0gY2xvdWRfZGYsIG1hcHBpbmcgPSBhZXMoeD1sYW1iZGEsIGNvbG9yID0gYXMuZmFjdG9yKGdlbmVyYXRpb24pKSkgKyBnZ3Bsb3QyOjpsYWJzKHg9VGVYKCIkXFxsYW1iZGEkIiksIHkgPSAiRGVuc2l0eSIsIGNvbG9yID0gIkdlbmVyYXRpb24iKSArIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMjApICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsgZ2dwbG90Mjo6Z2VvbV92bGluZShnZ3Bsb3QyOjphZXMoeGludGVyY2VwdD10cnVlX3Bhcm1zWywxXSksIGNvbG9yPSJibGFjayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIpKQpnZ3NhdmUoIi4uL2ZpZ3VyZXMvcG9zdGVyaW9yX2xhbWJkYV93b19yZWdfbWlzc19zdXBwX21hdC5wZGYiLCBwb3N0ZXJpb3JfbGFtYmRhKQoKKHBvc3Rlcmlvcl9tdSA8LSBnZ3Bsb3QyOjpnZ3Bsb3QoZGF0YSA9IGNsb3VkX2RmICkgKyBnZ3Bsb3QyOjpnZW9tX2RlbnNpdHkoZGF0YSA9IGNsb3VkX2RmLCBtYXBwaW5nID0gYWVzKHg9bXUsIGNvbG9yID0gYXMuZmFjdG9yKGdlbmVyYXRpb24pKSkgKyBnZ3Bsb3QyOjpsYWJzKHg9VGVYKCIkXFxtdSQiKSwgeSA9ICJEZW5zaXR5IiwgY29sb3IgPSAiR2VuZXJhdGlvbiIpICsgZ2dwbG90Mjo6dGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAyMCkgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikgKyBnZ3Bsb3QyOjpnZW9tX3ZsaW5lKGdncGxvdDI6OmFlcyh4aW50ZXJjZXB0PXRydWVfcGFybXNbLDJdKSwgY29sb3I9ImJsYWNrIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5ldHlwZT0iZGFzaGVkIikpCmdnc2F2ZSgiLi4vZmlndXJlcy9wb3N0ZXJpb3JfbXVfd29fcmVnX21pc3Nfc3VwcF9tYXQucGRmIiwgcG9zdGVyaW9yX211KQoKKHBvc3Rlcmlvcl9zaWcgPC0gZ2dwbG90Mjo6Z2dwbG90KGRhdGEgPSBjbG91ZF9kZiApICsgZ2dwbG90Mjo6Z2VvbV9kZW5zaXR5KGRhdGEgPSBjbG91ZF9kZiwgbWFwcGluZyA9IGFlcyh4PXNpZ21hLCBjb2xvciA9IGFzLmZhY3RvcihnZW5lcmF0aW9uKSkpICsgZ2dwbG90Mjo6bGFicyh4PVRlWCgiJFxcc2lnbWEkIiksIHkgPSAiRGVuc2l0eSIsIGNvbG9yID0gIkdlbmVyYXRpb24iKSArIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMjApICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsgZ2dwbG90Mjo6Z2VvbV92bGluZShnZ3Bsb3QyOjphZXMoeGludGVyY2VwdD10cnVlX3Bhcm1zWywzXSksIGNvbG9yPSJibGFjayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIpKQpnZ3NhdmUoIi4uL2ZpZ3VyZXMvcG9zdGVyaW9yX3NpZ193b19yZWdfbWlzc19zdXBwX21hdC5wZGYiLCBwb3N0ZXJpb3Jfc2lnKQpgYGAKYGBge3J9CiMgUHJpb3Igc2V0dGluZ3MgZm9yIHRoZSBwYXJhbWV0ZXJzCnByaW9yX2xhbWJkYSA8LSBwcmlvcl9kaXN0KCJ1bmlmb3JtIiwgImxhbWJkYSIsYygwLCA1KSkKcHJpb3JfbXUgPC0gcHJpb3JfZGlzdCgidW5pZm9ybSIsICJtdSIsYygtMywzKSkKcHJpb3Jfc2lnbWEgPC0gcHJpb3JfZGlzdCgidW5pZm9ybSIsICJzaWdtYSIsIGMoMCwgMikpCnByaW9yX2Rpc3RyaWJ1dGlvbnMgPC0gbGlzdChwcmlvcl9sYW1iZGEsIHByaW9yX211LCBwcmlvcl9zaWdtYSkKbW9kZWxfcHJpb3IgPC0gaW5kZXBlbmRlbnRfcHJpb3JzKHByaW9yX2Rpc3RyaWJ1dGlvbnMpCgojIFNldHRpbmdzIGZvciB0aGUgYWJjIGFsZ29yaXRobQpwb3BTaXplIDwtIDUwMDsgIE1DX1I8LSA1MDA7IGFjYyA8LSAwLjAwMTsgc3BfYm91bmRzIDwtIGMobWluKGRmX3BwJHBwIC8gZGF0YSR4KSwgbWF4KGRmX3BwJHBwIC8gZGF0YSR4KSkKZXBzX21pbiA8LSAwLjAxCiMgcmVzX2FiYyA8LSBhYmMoZGF0YSwgbW9kZWxfcHJpb3IsIGxfbSwgcF9wLCBwb3BTaXplLCBNQ19SLCBhY2MsIHNwX2JvdW5kcywgZXBzX21pbikKIyBzYXZlKHJlc19hYmMsIGZpbGUgPSAiLi4vZGF0YS9yZXNfYWJjX3dfcmVnX21pc3Nfc3VwcF9tYXRlcmlhbF9TZWN0aW9uMi5SRGF0YSIpCmBgYAoKYGBge3J9CmxvYWQoIi4uL2RhdGEvcmVzX2FiY193X3JlZ19taXNzX3N1cHBfbWF0ZXJpYWxfU2VjdGlvbjIuUkRhdGEiKQojIFdlIHJldHJpZXZlIHRoZSBjbG91ZCBvZiBwYXJ0aWNsZXMgYXNzb2NpYXRlZCB0byBlYWNoIGdlbmVyYXRpb24KY2xvdWRfZGZzIDwtIGxhcHBseShyZXNfYWJjJENsb3VkcywgYXMuZGF0YS5mcmFtZSkKY2xvdWRfZGYgPC0gZG8uY2FsbChyYmluZCwgY2xvdWRfZGZzKQpjbG91ZF9kZiRnZW5lcmF0aW9uIDwtIHNvcnQocmVwKDE6bGVuZ3RoKHJlc19hYmMkQ2xvdWRzKSwgcG9wU2l6ZSkpCmNsb3VkX2RmX3Bsb3QgPC0gcmJpbmQoCiAgZGF0YS5mcmFtZShwYXJtID0gY2xvdWRfZGYkbGFtYmRhLCBwYXJtX25hbWUgPSAibGFtYmRhIiwgZ2VuZXJhdGlvbiA9IGNsb3VkX2RmJGdlbmVyYXRpb24pLAogICAgZGF0YS5mcmFtZShwYXJtID0gY2xvdWRfZGYkc2lnbWEsIHBhcm1fbmFtZSA9ICJtdSIsIGdlbmVyYXRpb24gPSBjbG91ZF9kZiRnZW5lcmF0aW9uKSwKICBkYXRhLmZyYW1lKHBhcm0gPSBjbG91ZF9kZiRzaWdtYSwgcGFybV9uYW1lID0gInNpZ21hIiwgZ2VuZXJhdGlvbiA9IGNsb3VkX2RmJGdlbmVyYXRpb24pCikKY2xvdWRfZGZfcGxvdCRwYXJtX25hbWUgPC1hcy5mYWN0b3IoY2xvdWRfZGZfcGxvdCRwYXJtX25hbWUpIApsZXZlbHMoY2xvdWRfZGZfcGxvdCRwYXJtX25hbWUpIDwtIGMobGFtYmRhID0gVGVYKCIkXFxsYW1iZGEkIiksIG11ID0gVGVYKCIkXFxtdSQiKSwgIHNpZ21hID0gVGVYKCIkXFxzaWdtYSQiKSkKCihwb3N0ZXJpb3JfbGFtYmRhIDwtIGdncGxvdDI6OmdncGxvdChkYXRhID0gY2xvdWRfZGYgKSArIGdncGxvdDI6Omdlb21fZGVuc2l0eShkYXRhID0gY2xvdWRfZGYsIG1hcHBpbmcgPSBhZXMoeD1sYW1iZGEsIGNvbG9yID0gYXMuZmFjdG9yKGdlbmVyYXRpb24pKSkgKyBnZ3Bsb3QyOjpsYWJzKHg9VGVYKCIkXFxsYW1iZGEkIiksIHkgPSAiRGVuc2l0eSIsIGNvbG9yID0gIkdlbmVyYXRpb24iKSArIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMjApICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIpICsgZ2dwbG90Mjo6Z2VvbV92bGluZShnZ3Bsb3QyOjphZXMoeGludGVyY2VwdD10cnVlX3Bhcm1zWywxXSksIGNvbG9yPSJibGFjayIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXR5cGU9ImRhc2hlZCIpKQpnZ3NhdmUoIi4uL2ZpZ3VyZXMvcG9zdGVyaW9yX2xhbWJkYV93X3JlZ19taXNzX3N1cHBfbWF0LnBkZiIsIHBvc3Rlcmlvcl9sYW1iZGEpCgoocG9zdGVyaW9yX211IDwtIGdncGxvdDI6OmdncGxvdChkYXRhID0gY2xvdWRfZGYgKSArIGdncGxvdDI6Omdlb21fZGVuc2l0eShkYXRhID0gY2xvdWRfZGYsIG1hcHBpbmcgPSBhZXMoeD1tdSwgY29sb3IgPSBhcy5mYWN0b3IoZ2VuZXJhdGlvbikpKSArIGdncGxvdDI6OmxhYnMoeD1UZVgoIiRcXG11JCIpLCB5ID0gIkRlbnNpdHkiLCBjb2xvciA9ICJHZW5lcmF0aW9uIikgKyBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDIwKSArIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArIGdncGxvdDI6Omdlb21fdmxpbmUoZ2dwbG90Mjo6YWVzKHhpbnRlcmNlcHQ9dHJ1ZV9wYXJtc1ssMl0pLCBjb2xvcj0iYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiKSkKZ2dzYXZlKCIuLi9maWd1cmVzL3Bvc3Rlcmlvcl9tdV93X3JlZ19taXNzX3N1cHBfbWF0LnBkZiIsIHBvc3Rlcmlvcl9tdSkKCihwb3N0ZXJpb3Jfc2lnIDwtIGdncGxvdDI6OmdncGxvdChkYXRhID0gY2xvdWRfZGYgKSArIGdncGxvdDI6Omdlb21fZGVuc2l0eShkYXRhID0gY2xvdWRfZGYsIG1hcHBpbmcgPSBhZXMoeD1zaWdtYSwgY29sb3IgPSBhcy5mYWN0b3IoZ2VuZXJhdGlvbikpKSArIGdncGxvdDI6OmxhYnMoeD1UZVgoIiRcXHNpZ21hJCIpLCB5ID0gIkRlbnNpdHkiLCBjb2xvciA9ICJHZW5lcmF0aW9uIikgKyBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDIwKSArIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKSArIGdncGxvdDI6Omdlb21fdmxpbmUoZ2dwbG90Mjo6YWVzKHhpbnRlcmNlcHQ9dHJ1ZV9wYXJtc1ssM10pLCBjb2xvcj0iYmxhY2siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlPSJkYXNoZWQiKSkKZ2dzYXZlKCIuLi9maWd1cmVzL3Bvc3Rlcmlvcl9zaWdfd19yZWdfbWlzc19zdXBwX21hdC5wZGYiLCBwb3N0ZXJpb3Jfc2lnKQpgYGAK